# SCSI read disc structure C++ help



## Dr.Zoidberg (Nov 21, 2008)

Hello,

I am having a spot of bother implementing the read disc structure command. The error comes from transferring data from the returned buffer into a char to print.

Error:

invalid application of sizeof to a bit-field
invalid conversion from uchar to const char*
initializing argument 2 of char* strncpy(char*, const char*, size)


```
//Function to access disc structure
    void discStructure(HANDLE device)
    {
        readStructureCDB readStruct  = {0};   //Creates CDB structure
        SCSI_recvStructure recvStruct  = {0};  //receive data
        DWORD returnedBuf;           //Bytes received through DeviceIoControl
        
        unsigned char cmdLen[sizeof(SCSI_PASS_THROUGH_DIRECT) + 96]  = {0}; 
        SCSI_PASS_THROUGH_DIRECT * discStructureSCSIptd = (SCSI_PASS_THROUGH_DIRECT *) cmdLen; 
        
        typedef enum {
        DVD     = 0x00,
        Bluray = 0x01
            /* reserved */
        } media_Type;
        
        //Set disc struct CDB here
        readStruct.opCode = SCSIOP_discStructure;           
        readStruct.allocationLength = sizeof(recvStruct);  
        //readStruct.mediaType = media_Type;  
        //printf("%d", readStruct.opCode);
        memcpy(discStructureSCSIptd->Cdb, &readStruct, sizeof(readStruct));
        
        discStructureSCSIptd->DataBuffer             = &recvStruct;
        discStructureSCSIptd->DataTransferLength = sizeof(recvStruct);
        discStructureSCSIptd->DataIn                  = SCSI_IOCTL_DATA_IN;
        discStructureSCSIptd->CdbLength             = sizeof(readStruct);
        discStructureSCSIptd->Length                  = sizeof(SCSI_PASS_THROUGH_DIRECT);
        discStructureSCSIptd->SenseInfoLength     = sizeof(cmdLen) - sizeof(SCSI_PASS_THROUGH_DIRECT);
        discStructureSCSIptd->SenseInfoOffset      = sizeof(SCSI_PASS_THROUGH_DIRECT);
        discStructureSCSIptd->TimeOutValue         = 6000; 
        
        BOOL bRet = DeviceIoControl(device, 
                                                IOCTL_SCSI_PASS_THROUGH_DIRECT, 
                                                (LPVOID)&cmdLen,
                            sizeof(cmdLen),
                            (LPVOID)&cmdLen,
                            sizeof(cmdLen),
                            &returnedBuf,
                            NULL);
          
          DWORD error = GetLastError();
          if (error != ERROR_SUCCESS)
          {
            printf("carry_cdb(): last error = %08X\n", error);
          }
        
        if (bRet) {
            char readStructExtract[100];
            int i = 0;
            /*Prints info from drive*/
            //strncpy(readStructExtract, recvStruct.disk_category, i = sizeof(recvStruct.disk_category));
           // readStructExtract[i] = 0;
            printf("disc category: %u\n", recvStruct.disk_category);
                printf("disc category: %u\n", recvStruct.part_version);
            printf("disc category: %u\n", recvStruct.disc_size);
            printf("disc category: %u\n", recvStruct.max_rate);
            printf("disc category: %u\n", recvStruct.reserved0);
            
            strncpy(readStructExtract, recvStruct.disk_category, i = sizeof(recvStruct.disk_category));
            readStructExtract[i] = 0;
            printf("vendor id: %s\n", readStructExtract);        
            
          }
          return ;
        
        
    }
```
I don't think I've set the return structure up correctly. Its called SCSI_DVD_STRUCTURES_H. The guide I'm working from says set the data types to UCHAR. I've tried char and it doesn't change anything.


```
#include <windows.h>
#ifndef IOCTL_SCSI_PASS_THROUGH_DIRECT

#define IOCTL_SCSI_PASS_THROUGH_DIRECT 0x4D014
#define SCSI_IOCTL_DATA_OUT          0
#define SCSI_IOCTL_DATA_IN           1
#define SCSI_IOCTL_DATA_UNSPECIFIED  2

        //Check pointers - shouldnt be here
        typedef struct _SCSI_PASS_THROUGH_DIRECT{
          USHORT Length;
          UCHAR ScsiStatus;
          UCHAR PathId;
          UCHAR TargetId;
          UCHAR Lun;
          UCHAR CdbLength;
          UCHAR SenseInfoLength;
          UCHAR DataIn;
          ULONG DataTransferLength;
          ULONG TimeOutValue;
          void * DataBuffer;
          ULONG SenseInfoOffset;
          UCHAR Cdb[16];
        } SCSI_PASS_THROUGH_DIRECT;

#endif   //Termintes endif
        

        
        typedef struct _SCSI_INQUIRY_STD_DATA{
          UCHAR preipheral_device_type : 5;
          UCHAR peripheral_qualifier: 3;
          UCHAR rsvrd : 7;
          UCHAR rmb: 1;
          UCHAR version;
          UCHAR RESPONSE_DATA_FORMAT;                // 7 = AERC, 6 = Obsolete, 5 = NormACA, 4 = HiSup 3-0 = Response data format.
          // If ANSI Version = 0, this is ATAPI and bits 7-4 = ATAPI version.
          UCHAR        ADDITIONAL_LENGTH;                    // Number of additional bytes available in inquiry data
          UCHAR        SCCSReserved;                        // SCC-2 device flag and reserved fields
          UCHAR        flags1;                                // First byte of support flags
          UCHAR        flags2;                                // Second byte of support flags (Byte 7)
          char        vendor_id[8];
          char        product_id[16];
          char        product_revision_level[4];
        } SCSI_INQUIRY_STD_DATA;
        
        /*typedef struct SCSI_INQUIRY_STRCT_DATA{
            
        }SCSI_INQUIRY_STRCT_DATA;*/
        
        #ifndef _SCSI_DVD_STRUCTURES_H
#define _SCSI_DVD_STRUCTURES_H
        
        typedef struct _SCSI_recvStructure{
            /* from MMC specs */
            UCHAR disk_category        :4;
            UCHAR part_version        :4;
            UCHAR disc_size            :4;
            UCHAR max_rate            :4;
            UCHAR reserved0            :1;
            UCHAR layers_num        :2;
            UCHAR track            :1;
            UCHAR layer_type        :4;
            UCHAR linear_density        :4;
            UCHAR track_density        :4;
            UCHAR zero_1;
            UCHAR user_data_start        [3];
            UCHAR zero_2;
            UCHAR user_data_end        [3];
            UCHAR zero_3;
            UCHAR user_data_end_layer0    [3];
            UCHAR reserved1            :7;
            UCHAR burst_cutting_area    :1;
            UCHAR media_specific        [2031];
        }SCSI_recvStructure;    

            
#endif
        
        
        
        
#ifndef SCSIOP_INQUIRY


#define SCSIOP_INQUIRY      0x12
        
        
                //*Reference region code - chech data types - UCHAR etc...
        typedef struct _inquiryCDB{
          UCHAR OperationCode6;    //* 0x12 - SCSIOP_INQUIRY
          UCHAR Reserved1 : 5;
          UCHAR LogicalUnitNumber : 3;
          UCHAR PageCode;
          UCHAR IReserved;
          UCHAR AllocationLength;
          UCHAR Control;
        } inquiryCDB;
                    
#endif
        
#define SCSIOP_discStructure   0xAD
        
        typedef struct _readStructureCDB{
            UCHAR opCode;       //0xAD SCSIOP_discStructure
            UCHAR mediaType    :4;
            UCHAR reserved0     :4;
            UCHAR address       [4];  //[4]
            UCHAR layerNo;
            UCHAR format;
            UCHAR allocationLength  ;  //[2]
            UCHAR reserved1    :6;
            UCHAR agid            :2;   //check these etc
            UCHAR ctl;
        }readStructureCDB;
```
Any assistance would be appreciated!

Thanks


----------



## IMM (Feb 1, 2002)

You could get rid of the strncpy line 
// strncpy(readStructExtract, recvStruct.disk_category, i = sizeof(recvStruct.disk_category));
and do the following with the printf
_printf("vendor id: %i\n", recvStruct.disk_category);_
(that's %i if it's unreadable above)
http://msdn.microsoft.com/en-us/library/hf4y5e3w(VS.80).aspx

Alternately, with an include such as
_#include <iostream>_
and the line 
_using namespace std;_
before the start of your code in the main file you could write
_cout << "vendor id: " << recvStruct.disk_category << endl;_

or you could use itoa (or similar function) to put it in a char array .... etc.


----------



## Dr.Zoidberg (Nov 21, 2008)

Thanks for the reply. I tried "printf("disc category: %i\n", recvStruct.disk_category);" and it just prints "0"

Unfortunatly, I don't think using names spaces or itoa could solve this. Does this look ok for the return buffer, for the read disc structure command? I recall you helped get the inquiry command working.:up:


```
#define _SCSI_DVD_STRUCTURES_H
        
        typedef struct _SCSI_recvStructure{
            /* from MMC specs */
            UCHAR disk_category        :4;
            UCHAR part_version        :4;
            UCHAR disc_size            :4;
            UCHAR max_rate            :4;
            UCHAR reserved0            :1;
            UCHAR layers_num        :2;
            UCHAR track            :1;
            UCHAR layer_type        :4;
            UCHAR linear_density        :4;
            UCHAR track_density        :4;
            UCHAR zero_1;
            UCHAR user_data_start        [3];
            UCHAR zero_2;
            UCHAR user_data_end        [3];
            UCHAR zero_3;
            UCHAR user_data_end_layer0    [3];
            UCHAR reserved1            :7;
            UCHAR burst_cutting_area    :1;
            UCHAR media_specific        [2031];
        }SCSI_recvStructure;    

            
#endif
```
What I'm trying to do is get the SCSI read disc structure command to return infomation from the lead in area of a disc. I need to pass values from the return buffer to seperate char variables. My programs going to use this information to do various bits and bobs other than printf.

Thanks!


----------



## IMM (Feb 1, 2002)

> Thanks for the reply. I tried "printf("disc category: %i\n", recvStruct.disk_category);" and it just prints "0"


That's what it should print for something that is a number.

I noticed another problem - the line
readStruct.allocationLength = sizeof(recvStruct); 
should have been 
readStruct.allocationLength = sizeof(SCSI_recvStructure);
to avoid an error.

For the following posts you will need some specifications
http://www.13thmonkey.org/documentation/SCSI/
You could grab a bunch of them -- but the one I'll be referring to for tables is mmc5r04.pdf

The following links may also be of some use to you now and in the future.
If you can understand it -- you could consider using the libcdio source -- but it's very different from the way you have been approaching this.
http://www-soft.uni-regensburg.de/soft/pd/DEV-CPP/INCLUDE/DDK/SCSI.H
http://www.gnu.org/software/libcdio/libcdio.html


----------



## IMM (Feb 1, 2002)

Based on Table 399 of mmc5r04.pdf and some other sources - I made a header file which I used for the sample code in the next post.

```
//  dsk_struct.h

#define SCSIOP_discStructure   0xAD

#pragma pack(1)

typedef struct _DVD_DESCRIPTOR_HEADER {
  USHORT Length;        // bigEndian
  UCHAR Reserved[2];
} DVD_DESCRIPTOR_HEADER, *PDVD_DESCRIPTOR_HEADER;

typedef struct _DVD_LAYER_DESCRIPTOR {
  DVD_DESCRIPTOR_HEADER Header;
  UCHAR PartVersion : 4;
  UCHAR DiskCategory : 4;
  UCHAR MaximumRate : 4;
  UCHAR DiskSize : 4;
  UCHAR LayerType : 4;
  UCHAR TrackPath : 1;
  UCHAR NumberOfLayers : 2;
  UCHAR Reserved1 : 1;
  UCHAR TrackDensity : 4;
  UCHAR LinearDensity : 4;
  DWORD StartingDataSector;      // bigEndian
  DWORD EndDataSector;           // bigEndian
  DWORD EndLayerZeroSector;      // bigEndian
  UCHAR Reserved5 : 7;
  UCHAR BCAFlag : 1;
  UCHAR Reserved6;
  UCHAR MediaSpecific[2031];
} DVD_LAYER_DESCRIPTOR, *PDVD_LAYER_DESCRIPTOR;        

typedef struct _READ_DVD_STRUCTURE {
  UCHAR   OperationCode;
  UCHAR   Reserved1 : 5;
  UCHAR   Lun : 3;
  UCHAR   RMDBlockNumber[4];
  UCHAR   LayerNumber;
  UCHAR   Format;
  USHORT  AllocationLength;
  UCHAR   Reserved3 : 6;
  UCHAR   AGID : 2;
  UCHAR   Control;
} READ_DVD_STRUCTURE, *PREAD_DVD_STRUCTURE;

#pragma pack()
```
In truth the _DVD_LAYER_DESCRIPTOR struct just represents the data returned by the drive (just a blob described in the docs).
You should note that it could simply be an array of unsigned chars. If that were the case, we could pull out the values we need by selecting the appropriate byte(s) then bitshifing and bitmasking to get what we want.
In this case we've used bitfields (eg. BCAFlag : 1; ) to indicate that it's only one bit 
(see the table for the blob and Bit Fields for an explanation)
Because this is a 'blob' we must insure that the compiler doesn't try to "improve" the alignments in the struct -- hence the pragmas for packing. Some programmers might push and pop the packing - but I tried to stay with the method used in "scsi_struct.h"

I've used DWORDs where I could have used a byte array such as
DWORD StartingDataSector; // bigEndian
instead of 
UCHAR StartingDataSector[4]; 
The array is common -- you would simply handle it differently.


----------



## IMM (Feb 1, 2002)

The following code was done quickly and is not checked for errors (it's probably full of them), but I tried to stay close to what you had posted.
It assumes that the drive is capable of this command, has an appropriate DVD disk in it already, and that there aren't any quirks in the model's firmware etc., which can be a real pain to determine sometimes 
It's hard-coded for drive E:
In response to the comment about printf and numbers -- I've shown a simple method for outputting a string to represent the meaning of the 'number' for the DiskCategory data (see Table 400 in the pdf).
The simple Swap functions take big-endian values and make little-endian ones -- there are other ways to do this.


```
#include <stdio.h>
#include <windows.h>
#include "scsi_struct.h"
#include "dsk_struct.h"


USHORT ShortSwap( USHORT s ) {
  unsigned char b1, b2;
  b1 = s & 255;
  b2 = (s >> 8) & 255;
  return (b1 << 8) + b2; }

DWORD dwordSwap (DWORD i) {
  unsigned char b1, b2, b3, b4;
  b1 = (char)(i & 255);
  b2 = (char)(( i >> 8 ) & 255);
  b3 = (char)(( i>>16 ) & 255);
  b4 = (char)(( i>>24 ) & 255);
  return ((DWORD)b1 << 24) + ((DWORD)b2 << 16) + ((DWORD)b3 << 8) + b4;}


void discStructure(HANDLE device)
{
  READ_DVD_STRUCTURE   readStruct  = {0};    //Creates CDB structure
  DVD_LAYER_DESCRIPTOR recvStruct  = {0};    //receive data
  DWORD returnedBuf;           //Bytes received through DeviceIoControl
  char *DiskCats[] = {
     "DVD-ROM","DVD-RAM","DVD-R","DVD-RW","HD DVD-ROM","HD DVD-RAM","HD DVD-R","Reserved",
     "Reserved","DVD+RW","DVD+R","Reserved","Reserved","DVD+RW DL","DVD+R DL","Reserved"};

  unsigned char cmdLen[sizeof(SCSI_PASS_THROUGH_DIRECT) + 96]  = {0};
  SCSI_PASS_THROUGH_DIRECT * discStructureSCSIptd = (SCSI_PASS_THROUGH_DIRECT *) cmdLen;

  readStruct.OperationCode = SCSIOP_discStructure;
  readStruct.AllocationLength = sizeof(READ_DVD_STRUCTURE);
  readStruct.Format = 0x0;  // just for clarity
  memcpy(discStructureSCSIptd->Cdb, &readStruct, sizeof(readStruct));

  discStructureSCSIptd->DataBuffer         = &recvStruct;
  discStructureSCSIptd->DataTransferLength = sizeof(recvStruct);
  discStructureSCSIptd->DataIn             = SCSI_IOCTL_DATA_IN;
  discStructureSCSIptd->CdbLength          = sizeof(readStruct);
  discStructureSCSIptd->Length             = sizeof(SCSI_PASS_THROUGH_DIRECT);
  discStructureSCSIptd->SenseInfoLength    = sizeof(cmdLen) - sizeof(SCSI_PASS_THROUGH_DIRECT);
  discStructureSCSIptd->SenseInfoOffset    = sizeof(SCSI_PASS_THROUGH_DIRECT);
  discStructureSCSIptd->TimeOutValue       = 6000;

  BOOL bRet = DeviceIoControl(device, IOCTL_SCSI_PASS_THROUGH_DIRECT, (LPVOID)&cmdLen,
               sizeof(cmdLen),(LPVOID)&cmdLen,sizeof(cmdLen),&returnedBuf,NULL);

  DWORD error = GetLastError();
  if (error != ERROR_SUCCESS) { printf("carry_cdb(): last error = %08X\n", error); }

  if (bRet) {
    printf("\n");
    printf("Length             : %i bytes\n", ShortSwap(recvStruct.Header.Length));
    printf("DiskCategory       : 0x%X ==> %s MediaType\n", recvStruct.DiskCategory, DiskCats[recvStruct.DiskCategory]);
    printf("PartVersion        : 0x%X\n", recvStruct.PartVersion);
    printf("DiskSize           : %u\n", recvStruct.DiskSize);
    printf("MaximumRate        : 0x%X\n", recvStruct.MaximumRate);
    printf("LayerType          : %u\n", recvStruct.LayerType);
    printf("TrackPath          : 0x%X\n", recvStruct.TrackPath);
    printf("NumberOfLayers     : %u\n", recvStruct.NumberOfLayers);
    printf("TrackDensity       : %u\n", recvStruct.TrackDensity);
    printf("LinearDensity      : %u\n", recvStruct.LinearDensity);
    printf("StartingDataSector : %#08x\n", dwordSwap(recvStruct.StartingDataSector));
    printf("EndDataSector      : %#08x\n", dwordSwap(recvStruct.EndDataSector));
    printf("EndLayerZeroSector : 0x%X\n", dwordSwap(recvStruct.EndLayerZeroSector));
    printf("BCA flag           : %u\n", recvStruct.BCAFlag);

  }
  return ;
}

int main()
{
  HANDLE hdevice;
  char deviceName[20];
  strcpy(deviceName, "\\\\.\\e:");
  hdevice = CreateFile(deviceName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL, OPEN_EXISTING, 0, 0);
  if(hdevice == INVALID_HANDLE_VALUE) {        //Error opening drive
    printf("Error accessing rom drive - creating handle (%d)\n", GetLastError());
    return 1; }
  printf("Device : %s\n\n", deviceName);
  discStructure(hdevice);
  CloseHandle(hdevice);
  return 0;
}
```
The "scsi_struct.h" header is the one referenced in my first reply in
http://forums.techguy.org/software-development/809038-scsi-programming-pointer-help.html


----------



## Dr.Zoidberg (Nov 21, 2008)

She works. Thanks! :up:


----------



## IMM (Feb 1, 2002)

You can (if you want) remove the
_UCHAR MediaSpecific[2031];_
line in the header. The request for a limited size of return (given to it with the size of the struct in the request) should be honoured.


----------

