diff --git a/Controllers/SeagateController/RGBController_Seagate.cpp b/Controllers/SeagateController/RGBController_Seagate.cpp new file mode 100644 index 00000000..5c53fd0b --- /dev/null +++ b/Controllers/SeagateController/RGBController_Seagate.cpp @@ -0,0 +1,168 @@ +/*-----------------------------------------*\ +| RGBController_Seagate.cpp | +| | +| Generic RGB Interface for Seagate | +| | +| Adam Honse (CalcProgrammer1) 11/8/2022 | +\*-----------------------------------------*/ + +#include "RGBController_Seagate.h" + +/**------------------------------------------------------------------*\ + @name Seagate + @category Storage + @type SCSI + @save :white_check_mark: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectSeagateControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_Seagate::RGBController_Seagate(SeagateController* controller_ptr) +{ + controller = controller_ptr; + + name = "Seagate Device"; + vendor = "Seagate"; + type = DEVICE_TYPE_STORAGE; + description = "Seagate Device"; + location = controller->GetLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = SEAGATE_MODE_STATIC; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Blink; + Blink.name = "Flashing"; + Blink.value = SEAGATE_MODE_BLINK; + Blink.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Blink.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Blink); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = SEAGATE_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Breathing.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Breathing); + + mode Spectrum; + Spectrum.name = "Spectrum Cycle"; + Spectrum.value = SEAGATE_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_RANDOM_COLOR | MODE_FLAG_MANUAL_SAVE; + Spectrum.color_mode = MODE_COLORS_RANDOM; + modes.push_back(Spectrum); + + SetupZones(); +} + +RGBController_Seagate::~RGBController_Seagate() +{ + delete controller; +} + +void RGBController_Seagate::SetupZones() +{ + zone led_zone; + led_zone.name = "LED Strip"; + led_zone.type = ZONE_TYPE_LINEAR; + led_zone.leds_min = 6; + led_zone.leds_max = 6; + led_zone.leds_count = 6; + led_zone.matrix_map = NULL; + zones.push_back(led_zone); + + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = "LED Strip LED"; + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_Seagate::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_Seagate::DeviceUpdateLEDs() +{ + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + UpdateSingleLED(led_idx); + } +} + +void RGBController_Seagate::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_Seagate::UpdateSingleLED(int led) +{ + unsigned char red = RGBGetRValue(colors[led]); + unsigned char grn = RGBGetGValue(colors[led]); + unsigned char blu = RGBGetBValue(colors[led]); + + switch(modes[active_mode].value) + { + case SEAGATE_MODE_STATIC: + controller->SetLEDStatic(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_BLINK: + controller->SetLEDBlink(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_BREATHING: + controller->SetLEDBreathing(led, red, grn, blu, false); + break; + + case SEAGATE_MODE_SPECTRUM: + controller->SetLEDsSpectrum(led, false); + break; + } +} + +void RGBController_Seagate::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} + +void RGBController_Seagate::DeviceSaveMode() +{ + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + switch(modes[active_mode].value) + { + case SEAGATE_MODE_STATIC: + controller->SetLEDStatic(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_BLINK: + controller->SetLEDBlink(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_BREATHING: + controller->SetLEDBreathing(led_idx, red, grn, blu, true); + break; + + case SEAGATE_MODE_SPECTRUM: + controller->SetLEDsSpectrum(led_idx, true); + break; + } + } +} diff --git a/Controllers/SeagateController/RGBController_Seagate.h b/Controllers/SeagateController/RGBController_Seagate.h new file mode 100644 index 00000000..d946d1f8 --- /dev/null +++ b/Controllers/SeagateController/RGBController_Seagate.h @@ -0,0 +1,33 @@ +/*-----------------------------------------*\ +| RGBController_Seagate.h | +| | +| Generic RGB Interface for Seagate | +| | +| Adam Honse (CalcProgrammer1) 11/8/2022 | +\*-----------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "SeagateController.h" + +class RGBController_Seagate : public RGBController +{ +public: + RGBController_Seagate(SeagateController* controller_ptr); + ~RGBController_Seagate(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + void DeviceSaveMode(); + +private: + SeagateController* controller; +}; diff --git a/Controllers/SeagateController/SeagateController.cpp b/Controllers/SeagateController/SeagateController.cpp new file mode 100644 index 00000000..a596fa31 --- /dev/null +++ b/Controllers/SeagateController/SeagateController.cpp @@ -0,0 +1,222 @@ +/*-----------------------------------------*\ +| SeagateController.cpp | +| | +| Code for Seagate Firecuda External HDD | +| RGB controller | +| | +| Adam Honse (CalcProgrammer1) 6/15/2023 | +\*-----------------------------------------*/ + +#include "SeagateController.h" + +SeagateController::SeagateController(scsi_device* dev_handle, char* path) +{ + this->dev = dev_handle; + this->path = path; +} + +SeagateController::~SeagateController() +{ + scsi_close(dev); +} + +std::string SeagateController::GetLocation() +{ + std::string str(path.begin(), path.end()); + return("SCSI: " + str); +} + +void SeagateController::SetLEDBlink + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x10] = {0}; + data[0] = 0x10; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_BLINK; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = 0x10; + data[10] = 0x10; + data[11] = r; + data[12] = g; + data[13] = b; + data[14] = 0xFF; + data[15] = 0xFF; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x10); +} + +void SeagateController::SetLEDBreathing + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x14] = {0}; + data[0] = 0x14; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_BREATHING; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = 0x0F; + data[10] = 0x0F; + data[11] = 0x0F; + data[12] = 0x0F; + data[13] = r; + data[14] = g; + data[15] = b; + data[16] = 0xFF; + data[17] = 0xFF; + data[18] = 0xFF; + data[19] = 0x00; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x14); +} + +void SeagateController::SetLEDsSpectrum + ( + unsigned char led_id, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x0A] = {0}; + data[0] = 0x0A; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_SPECTRUM; + data[8] = 0x02; + data[9] = 0xB4; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x0A); +} + +void SeagateController::SetLEDStatic + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold RGB control data | + \*-----------------------------------------------------------------------------*/ + unsigned char data[0x0E] = {0}; + data[0] = 0x0E; /* size of data packet */ + data[1] = 0x00; + data[2] = 0x01; + data[3] = 0x09; + data[4] = 0x01; + data[5] = 0x06; + data[6] = led_id; + data[7] = SEAGATE_MODE_STATIC; + if(save) + { + data[8] = 0x03; /* 0x00 for no save, 0x03 for */ + /* save */ + } + else + { + data[8] = 0x00; + } + data[9] = r; + data[10] = g; + data[11] = b; + data[12] = 0xFF; + data[13] = 0xFF; + + /*-----------------------------------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------------------------------*/ + SendPacket(data, 0x0E); +} + +void SeagateController::SendPacket + ( + unsigned char * packet, + unsigned char packet_sz + ) +{ + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold CDB | + \*-----------------------------------------------------------------------------*/ + unsigned char cdb[12] = {0}; + cdb[0] = 0xD2; + cdb[1] = 0x53; /* S */ + cdb[2] = 0x65; /* e */ + cdb[3] = 0x74; /* t */ + cdb[4] = 0x4C; /* L */ + cdb[5] = 0x65; /* e */ + cdb[6] = 0x64; /* d */ + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = 0x30; + cdb[10] = packet_sz; + cdb[11] = 0x00; + + /*-----------------------------------------------------------------------------*\ + | Create buffer to hold sense data | + \*-----------------------------------------------------------------------------*/ + unsigned char sense[32] = {0}; + + /*-----------------------------------------------------------------------------*\ + | Write SCSI packet | + \*-----------------------------------------------------------------------------*/ + scsi_write(dev, packet, packet_sz, cdb, 12, sense, 32); +} diff --git a/Controllers/SeagateController/SeagateController.h b/Controllers/SeagateController/SeagateController.h new file mode 100644 index 00000000..09f27491 --- /dev/null +++ b/Controllers/SeagateController/SeagateController.h @@ -0,0 +1,73 @@ +/*-----------------------------------------*\ +| SeagateController.h | +| | +| Definitions for Seagate Firecuda | +| External HDD RGB controller | +| | +| Adam Honse (CalcProgrammer1) 6/15/2023 | +\*-----------------------------------------*/ + +#pragma once + +#include +#include "scsiapi.h" + +enum +{ + SEAGATE_MODE_STATIC = 0x01, /* Static mode */ + SEAGATE_MODE_BLINK = 0x02, /* Blink mode */ + SEAGATE_MODE_BREATHING = 0x03, /* Breathing mode */ + SEAGATE_MODE_SPECTRUM = 0x05, /* Spectrum mode */ +}; + +class SeagateController +{ +public: + SeagateController(scsi_device* dev_handle, char* path); + ~SeagateController(); + + std::string GetLocation(); + + void SetLEDBlink + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + + void SetLEDBreathing + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + + void SetLEDsSpectrum + ( + unsigned char led_id, + bool save + ); + + void SetLEDStatic + ( + unsigned char led_id, + unsigned char r, + unsigned char g, + unsigned char b, + bool save + ); + +private: + scsi_device* dev; + std::string path; + + void SendPacket + ( + unsigned char * packet, + unsigned char packet_sz + ); +}; diff --git a/Controllers/SeagateController/SeagateControllerDetect.cpp b/Controllers/SeagateController/SeagateControllerDetect.cpp new file mode 100644 index 00000000..ad3f69fb --- /dev/null +++ b/Controllers/SeagateController/SeagateControllerDetect.cpp @@ -0,0 +1,43 @@ +#include "Detector.h" +#include "LogManager.h" +#include "SeagateController.h" +#include "RGBController.h" +#include "RGBController_Seagate.h" +#include + +#include "scsiapi.h" + +/******************************************************************************************\ +* * +* DetectSeagateControllers * +* * +* Detects Seagate FireCuda HDD devices * +* * +\******************************************************************************************/ + +void DetectSeagateControllers() +{ + scsi_device_info * info = scsi_enumerate(NULL, NULL); + + while(info) + { + if(strncmp(info->vendor, "Seagate", 7) == 0 && strncmp(info->product, "FireCuda HDD", 12) == 0) + { + scsi_device * dev = scsi_open_path(info->path); + + if(dev) + { + SeagateController* controller = new SeagateController(dev, info->path); + RGBController_Seagate* rgb_controller = new RGBController_Seagate(controller); + + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + info = info->next; + } + + scsi_free_enumeration(info); + +} /* DetectSeagateControllers() */ + +REGISTER_DETECTOR("Seagate Firecuda HDD", DetectSeagateControllers); diff --git a/OpenRGB.pro b/OpenRGB.pro index 03c27156..0337762c 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -79,6 +79,7 @@ INCLUDEPATH += i2c_tools/ \ net_port/ \ pci_ids/ \ + scsiapi/ \ serial_port/ \ super_io/ \ AutoStart/ \ @@ -206,6 +207,7 @@ INCLUDEPATH += Controllers/RedSquareKeyroxController/ \ Controllers/RoccatController/ \ Controllers/SapphireGPUController/ \ + Controllers/SeagateController/ \ Controllers/SinowealthController/ \ Controllers/SonyGamepadController/ \ Controllers/SRGBmodsController/ \ @@ -300,6 +302,7 @@ HEADERS += qt/OpenRGBYeelightSettingsPage/OpenRGBYeelightSettingsPage.h \ qt/OpenRGBZoneResizeDialog/OpenRGBZoneResizeDialog.h \ qt/OpenRGBZonesBulkResizer/OpenRGBZonesBulkResizer.h \ + scsiapi/scsiapi.h \ serial_port/find_usb_serial_port.h \ serial_port/serial_port.h \ StringUtils.h \ @@ -688,6 +691,8 @@ HEADERS += Controllers/SapphireGPUController/SapphireNitroGlowV3Controller.h \ Controllers/SapphireGPUController/RGBController_SapphireNitroGlowV1.h \ Controllers/SapphireGPUController/RGBController_SapphireNitroGlowV3.h \ + Controllers/SeagateController/SeagateController.h \ + Controllers/SeagateController/RGBController_Seagate.h \ Controllers/SinowealthController/SinowealthController.h \ Controllers/SinowealthController/SinowealthController1007.h \ Controllers/SinowealthController/SinowealthGMOWController.h \ @@ -1377,6 +1382,9 @@ SOURCES += Controllers/SapphireGPUController/SapphireGPUControllerDetect.cpp \ Controllers/SapphireGPUController/RGBController_SapphireNitroGlowV1.cpp \ Controllers/SapphireGPUController/RGBController_SapphireNitroGlowV3.cpp \ + Controllers/SeagateController/SeagateController.cpp \ + Controllers/SeagateController/SeagateControllerDetect.cpp \ + Controllers/SeagateController/RGBController_Seagate.cpp \ Controllers/SinowealthController/SinowealthController.cpp \ Controllers/SinowealthController/SinowealthController1007.cpp \ Controllers/SinowealthController/SinowealthGMOWController.cpp \ @@ -1660,6 +1668,7 @@ win32:SOURCES += i2c_smbus/i2c_smbus_nct6775.cpp \ i2c_smbus/i2c_smbus_nvapi.cpp \ i2c_smbus/i2c_smbus_piix4.cpp \ + scsiapi/scsiapi_windows.c \ serial_port/find_usb_serial_port_win.cpp \ wmi/wmi.cpp \ AutoStart/AutoStart-Windows.cpp \ @@ -1834,6 +1843,7 @@ contains(QMAKE_PLATFORM, linux) { SOURCES += \ dependencies/hueplusplus-1.0.0/src/LinHttpHandler.cpp \ i2c_smbus/i2c_smbus_linux.cpp \ + scsiapi/scsiapi_linux.c \ serial_port/find_usb_serial_port_linux.cpp \ AutoStart/AutoStart-Linux.cpp \ Controllers/AsusTUFLaptopController/AsusTUFLaptopLinuxController.cpp \ @@ -2039,6 +2049,9 @@ macx:contains(QMAKE_HOST.arch, arm64) { INCLUDEPATH += \ /opt/homebrew/include \ + SOURCES += \ + scsiapi/scsiapi_macos.c \ + LIBS += \ -L/opt/homebrew/lib \ } @@ -2054,6 +2067,7 @@ macx:contains(QMAKE_HOST.arch, x86_64) { SOURCES += \ i2c_smbus/i2c_smbus_i801.cpp \ + scsiapi/scsiapi_macos.c \ HEADERS += \ dependencies/macUSPCIO/macUSPCIOAccess.h \ diff --git a/scsiapi/scsiapi.h b/scsiapi/scsiapi.h new file mode 100644 index 00000000..60e5b5b7 --- /dev/null +++ b/scsiapi/scsiapi.h @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| scsiapi.h | +| | +| Cross-platform SCSI access library | +| | +| Adam Honse 7/28/2023 | +\*---------------------------------------------------------*/ + +#pragma once + +/*---------------------------------------------------------*\ +| Includes | +\*---------------------------------------------------------*/ +#include + +/*---------------------------------------------------------*\ +| Platform-specific Includes | +\*---------------------------------------------------------*/ +#ifdef WIN32 +#include +#include +#include +#include "WinIoCtl.h" +#endif + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef __APPLE__ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------*\ +| SCSI Device type | +\*---------------------------------------------------------*/ +struct scsi_device_info +{ + char * path; /* path string of SCSI device */ + char * vendor; /* vendor string of SCSI device */ + char * product; /* product string of SCSI device */ + struct scsi_device_info * next; /* pointer to next scsi_device_info */ +}; + +struct scsi_device +{ + /*-----------------------------------------------------*\ + | SCSI device handle/file descriptor | + \*-----------------------------------------------------*/ +#ifdef WIN32 + HANDLE fd; +#else + int fd; +#endif + +}; + +/*---------------------------------------------------------*\ +| Functions | +\*---------------------------------------------------------*/ +void scsi_close(struct scsi_device * dev); + +struct scsi_device_info * scsi_enumerate(const char * vendor, const char * product); + +void scsi_free_enumeration(struct scsi_device_info * devs); + +struct scsi_device * scsi_open_path(const char *path); + +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); + +#ifdef __cplusplus +} +#endif diff --git a/scsiapi/scsiapi_linux.c b/scsiapi/scsiapi_linux.c new file mode 100644 index 00000000..052ef7b7 --- /dev/null +++ b/scsiapi/scsiapi_linux.c @@ -0,0 +1,225 @@ +/*---------------------------------------------------------*\ +| scsiapi_linux.c | +| | +| Cross-platform SCSI access library | +| Linux implementation | +| | +| Adam Honse 7/28/2023 | +\*---------------------------------------------------------*/ + +#pragma once + +/*---------------------------------------------------------*\ +| Includes | +\*---------------------------------------------------------*/ +#include +#include +#include + +#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 | + \*-------------------------------------------------*/ + read(sg_model_fd, sg_model_buf, 512); + close(sg_model_fd); + + for(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 | + \*-------------------------------------------------*/ + read(sg_vendor_fd, sg_vendor_buf, 512); + close(sg_vendor_fd); + + for(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 = data; + header.cmdp = cdb; + header.sbp = sense; + header.timeout = 20000; + header.flags = 0; + + /*-----------------------------------------------------*\ + | Send pass through command | + \*-----------------------------------------------------*/ + ioctl(dev->fd, SG_IO, &header); +} + +#ifdef __cplusplus +} +#endif diff --git a/scsiapi/scsiapi_macos.c b/scsiapi/scsiapi_macos.c new file mode 100644 index 00000000..6d9cb3d3 --- /dev/null +++ b/scsiapi/scsiapi_macos.c @@ -0,0 +1,68 @@ +/*---------------------------------------------------------*\ +| scsiapi_macos.c | +| | +| Cross-platform SCSI access library | +| MacOS implementation (NON-FUNCTIONAL) | +| | +| Adam Honse 7/28/2023 | +\*---------------------------------------------------------*/ + +#pragma once + +/*---------------------------------------------------------*\ +| Includes | +\*---------------------------------------------------------*/ +#include +#include +#include + +#include "scsiapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------*\ +| Functions | +\*---------------------------------------------------------*/ + +void scsi_close(struct scsi_device * dev) +{ + +} + +struct scsi_device_info * scsi_enumerate(const char * vendor, const char * product) +{ + return(NULL); +} + +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) +{ + return(NULL); +} + +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) +{ + +} + +#ifdef __cplusplus +} +#endif diff --git a/scsiapi/scsiapi_windows.c b/scsiapi/scsiapi_windows.c new file mode 100644 index 00000000..e9619110 --- /dev/null +++ b/scsiapi/scsiapi_windows.c @@ -0,0 +1,241 @@ +/*---------------------------------------------------------*\ +| scsiapi.c | +| | +| Cross-platform SCSI access library | +| Windows implementation | +| | +| Adam Honse 7/28/2023 | +\*---------------------------------------------------------*/ + +#pragma once + +/*---------------------------------------------------------*\ +| Includes | +\*---------------------------------------------------------*/ +#include +#include +#include + +#include "scsiapi.h" + +#define DEVBUFSIZE (128 * 1024) + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------*\ +| Functions | +\*---------------------------------------------------------*/ + +void scsi_close(struct scsi_device * dev) +{ + +} + +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; + + char buff[DEVBUFSIZE] = ""; + int char_count; + + /*-----------------------------------------------------*\ + | Query all devices and look for SCSI devices | + \*-----------------------------------------------------*/ + char_count = QueryDosDevice(NULL, buff, DEVBUFSIZE); + + if(char_count == 0) + { + return 0; + } + + for(int i = 0; i < char_count; i++) + { + char * buf_ptr = buff + i; + + if(strstr(buf_ptr, "SCSI")) + { + /*---------------------------------------------*\ + | Extract vendor and product from SCSI path | + | Format: SCSI#Disk&Ven_VENDOR&Prod_PRODUCT#... | + \*---------------------------------------------*/ + char c_vendor[512]; + char c_product[512]; + char c_path[MAX_PATH]; + + sscanf(buf_ptr, "SCSI#Disk&Ven_%[^&]&Prod_%[^#]#", c_vendor, c_product); + + strcpy(c_path, "\\\\?\\"); + strncat(c_path, buf_ptr, MAX_PATH - 4); + + /*---------------------------------------------*\ + | Windows converts spaces to underscores so | + | undo that | + | There may be a better way to do this... | + \*---------------------------------------------*/ + for(int pos = 0; pos < strlen(c_vendor); pos++) + { + if(c_vendor[pos] == '_') + { + c_vendor[pos] = ' '; + } + } + + for(int pos = 0; pos < strlen(c_product); pos++) + { + if(c_product[pos] == '_') + { + c_product[pos] = ' '; + } + } + + /*---------------------------------------------*\ + | 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(c_product, product, strlen(product)) == 0) + { + if(strncmp(c_vendor, 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(c_path) + 1); + strcpy(info->path, c_path); + + info->vendor = malloc(strlen(c_vendor) + 1); + strcpy(info->vendor, c_vendor); + + info->product = malloc(strlen(c_product) + 1); + strcpy(info->product, c_product); + + info->next = NULL; + + if(ret_ptr == NULL) + { + ret_ptr = info; + } + else + { + last_ptr->next = info; + } + + last_ptr = info; + } + } + + i += strlen(buff + i); + } + + 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) +{ + HANDLE device_fd = CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, (LPSECURITY_ATTRIBUTES)0x0, OPEN_EXISTING, 0x0, (HANDLE)0x0); + + struct scsi_device * device = NULL; + + if(device_fd != INVALID_HANDLE_VALUE) + { + 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 SCSI_PASS_THROUGH_DIRECT | + | Size must be enough for the SCSI_PASS_THROUGH_DIRECT | + | struct plus the sense data. | + \*-----------------------------------------------------*/ + int buffer_length = (sizeof(SCSI_PASS_THROUGH_DIRECT) + sense_length); + unsigned char * buffer = malloc(buffer_length); + + /*-----------------------------------------------------*\ + | Zero out the buffer | + \*-----------------------------------------------------*/ + memset(buffer, 0, buffer_length); + + /*-----------------------------------------------------*\ + | Create PSCSI_PASS_THROUGH_DIRECT pointer and point it | + | to the buffer | + \*-----------------------------------------------------*/ + PSCSI_PASS_THROUGH_DIRECT command = (PSCSI_PASS_THROUGH_DIRECT)buffer; + + /*-----------------------------------------------------*\ + | Set up pass through command | + \*-----------------------------------------------------*/ + command->Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + command->ScsiStatus = 0x00; + command->PathId = 0x00; + command->TargetId = 0x00; + command->Lun = 0x00; + command->CdbLength = cdb_length; + command->SenseInfoLength = sense_length; + command->DataIn = SCSI_IOCTL_DATA_OUT; + command->DataTransferLength = data_length; + command->TimeOutValue = 0x00000014; + command->DataBuffer = data; + command->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT); + + /*-----------------------------------------------------*\ + | Copy CDB and sense data into buffer | + \*-----------------------------------------------------*/ + memcpy(command->Cdb, cdb, cdb_length); + memcpy(&buffer[sizeof(SCSI_PASS_THROUGH_DIRECT)], sense, sense_length); + + /*-----------------------------------------------------*\ + | Send pass through command | + \*-----------------------------------------------------*/ + DeviceIoControl(dev->fd, IOCTL_SCSI_PASS_THROUGH_DIRECT, command, buffer_length, command, buffer_length, NULL, NULL); + + /*-----------------------------------------------------*\ + | Copy sense data out of buffer | + \*-----------------------------------------------------*/ + memcpy(sense, &buffer[sizeof(SCSI_PASS_THROUGH_DIRECT)], sense_length); + + /*-----------------------------------------------------*\ + | Free the buffer | + \*-----------------------------------------------------*/ + free(buffer); +} +#ifdef __cplusplus +} +#endif