Click here to Skip to main content
15,888,454 members
Articles / General Programming

Magnetic Tape Data Storage. Part 2: Media Changer - Status Commands

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
28 Oct 2010CPOL2 min read 28.2K   6   12
Media changer status commands implementations in C#

Introduction

This article describes the procedures and issues involved in the development of software applications to communicate with the Media Changer and to obtain basic information about its internal structure.

Discovering Changer Name

The first operation that should be done is to create OS handle to Media changer. Handle can be obtained by CreateFile API.

C#
[DllImport("kernel32.dll", CharSet = CharSet.Auto, 
	CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern SafeFileHandle CreateFile(
          uint dwDesiredAccess,
          uint dwShareMode,
          IntPtr SecurityAttributes,
          uint dwCreationDisposition,
          uint dwFlagsAndAttributes,
          IntPtr hTemplateFile
          ); 

As you can see, the first parameter is file name. Most attached hardware devices get unique system names, a.k.a. DOS name. Media changer can get the following system names: MediaChangeX, ChangerX. To obtain the exact name, we can call the Win API method:

C#
[DllImport("kernel32.dll")]
   static extern uint QueryDosDevice(string lpDeviceName,
   IntPtr lpTargetPath, uint ucchMax);

   private static string QueryDosDevice()
   {
       IntPtr mem = Marshal.AllocHGlobal(1024);

       try
       {
           if (mem == IntPtr.Zero)
               throw new Win32Exception("Failed to allocate unmanaged memory");

           uint returnSize = (int)QueryDosDevice(null, mem, (uint)maxSize);
           if (returnSize == 0)
               throw new Win32Exception("Failed to obtain DOS devices info");

           string allDevices = Marshal.PtrToStringAnsi(mem, returnSize);
           return allDevices;
       }
       finally
       {
           if(mem != IntPtr.Zero )
               Marshal.FreeHGlobal(mem);
       }
   }
   // Output(Fragment)
   // ...
   //Scsi3:
   //DISPLAY3
   //ACPI#GenuineIntel_-_EM64T_Family_6_Model_44#12#{97fadb10-4e33-40ae-359c-8bef029dbdd0}
   //ACPI#GenuineIntel_-_EM64T_Family_6_Model_44#_8#{97fadb10-4e33-40ae-359c-8bef029dbdd0}
   //ACPI#IPI0001#5#{4116f60b-25b3-4662-b732-99a6111edc0b}
   //Root#*ISATAP#0002#{cac88484-7515-4c03-82e6-71a87abac361}
   //FltMgrMsg
   //HID#VID_0624&PID_0248&MI_00#7&2c1758c0&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
   //ACPI#GenuineIntel_-_EM64T_Family_6_Model_44#_2#{97fadb10-4e33-40ae-359c-8bef029dbdd0}
   //B06BDRV#L2ND&PCI_163914E4&SUBSYS_02351028&REV_20#5&3785ac6a&0&20050200#{cac88484-
   //7515-4c03-82e6-71a87abac361}
   //STORAGE#Volume#1&19f7e59c&0&GptPartition{a132face-928a-46a7-9bd2-e0351c99588e}#
   //{7f108a28-9833-4b3b-b780-2c6b5fa5c062}
   //Changer0
   //Nm3_{C41A3799-2F0C-471E-88E3-EE5BBA8E487E}-
   //{6E022F38-AB31-44C5-8206-2EB023EFF145}-0000
   //USB#ROOT_HUB20#4&37084163&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
   // ...

The returned string will contain all system DOS names. Search for Changer or MediaChanger words. You will find the exact name for your Media Changer. If you have two or more Media Changers attached to your machine, you can use DeviceIOControl to obtain address information, such as the target ID (TID) and the logical unit number (LUN) of a particular SCSI target. So you will get the information whose DOS name is allocated for device that you are working with.

C#
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool DeviceIoControl(
        SafeFileHandle hDevice,
        uint dwIoControlCode,
        IntPtr lpInBuffer,
        uint nInBufferSize,
        IntPtr lpOutBuffer,
        uint nOutBufferSize,
        out uint lpBytesReturned,
        IntPtr lpOverlapped);

    private static string MapDosName(string fileHandle)
    {
        IntPtr ptr = Marshal.AllocHGlobal(1024);
        try
        {
            bool status = SCSI.DeviceIoControl(
                    fileHandle, 
                    SCSI.IOCTL_SCSI_GET_ADDRESS,
                    IntPtr.Zero,
                    0,
                    ptr,
                    1024,
                    out returned,
                    IntPtr.Zero);

            if (status == false)
            {
                return string.Format("Error opening the DEVICE {0}", 
			Marshal.GetLastWin32Error());
            }

            return string.Format("Length={0}, PortNumber={1}, PathId={2}, 
			TargetId={3}, Lun={4}",
                Marshal.ReadInt32(ptr, 0),
                Marshal.ReadByte(ptr, 4),
                Marshal.ReadByte(ptr, 5),
                Marshal.ReadByte(ptr, 6),
                Marshal.ReadByte(ptr, 7));
        }
        finally
        {
            Marshal.Release(ptr);
        }
    }
    // Output (fileHandle is created from \\.\Changer0):
    //
    // Changer0
    // Length:8, TargetID:2, LUN:1, PathID:0, Port:3
    // 

Discovering Changer Structure

Now, we have enough information to work with Media Changer Device, so we will focus on the API function that causes a SCSI BUS Changer device to return information about itself. For example, some devices can have the following internal structure (logical):

Tape_structure.jpg

As you remember, magnetic tape data storage is an integration of two separate devices, a tape drive and a media changer. The media changer consists of all the mechanics and electronics required to store and move tape cartridges while the tape drive provides the read/write functionality. So when tape is inserted to the devices it goes to the Mail Slot. From mail slot, it can be moved to regular slot (slots 1-8) or Drive. When the tape is in Drive, it is possible to perform IO operations. Let’s see how discovering can be done by C# code. One of the possible ways is performing IOCTL_CHANGER_GET_ELEMENT_STATUS call. See Discover method, this method will type status for each existing regular slot and for the drive.

C#
public Changer.ChangerElementStatusEx
            GetElementStatus(uint address, Changer.ElementType elementType)
    {
        IntPtr ptrIn = IntPtr.Zero;
        IntPtr ptrOut = IntPtr.Zero;
        try
        {
            m_Log.Debug("ENTER GetElementStatus");
            Changer.ChangerReadElementStatus readStatusIn = 
			new Changer.ChangerReadElementStatus();

            readStatusIn.ElementList.NumberOfElements = 1;
            readStatusIn.ElementList.Element.ElementAddress = address;
            readStatusIn.ElementList.Element.ElementType = elementType;
            readStatusIn.VolumeTagInfo = 1;

            ptrIn = Marshal.AllocHGlobal(Marshal.SizeOf(readStatusIn));
            Marshal.StructureToPtr(readStatusIn, ptrIn, true);

            Changer.ChangerElementStatusEx driveStatus = 
			new Changer.ChangerElementStatusEx();
            ptrOut = Marshal.AllocHGlobal(Marshal.SizeOf(driveStatus));

            uint readBytes;
            if (false == Changer.DeviceIoControl(
                ChangerHandle,                                      // handle to device
                (uint)Changer.IOCTL_CHANGER_GET_ELEMENT_STATUS,     // dwIoControlCode
                ptrIn,                                              // lpInBuffer
                (uint)Marshal.SizeOf(readStatusIn),                 // nInBufferSize
                ptrOut,                                             // output buffer
                (uint)Marshal.SizeOf(driveStatus),         // size of output buffer
                out readBytes,                             // number of bytes returned
                IntPtr.Zero                                // OVERLAPPED structure
            ))
            {
                int error = Marshal.GetLastWin32Error();
                throw new Win32Exception(error, "Cannot access device, error:" + error);
            }

            driveStatus = (Changer.ChangerElementStatusEx)Marshal.PtrToStructure
			(ptrOut, driveStatus.GetType());

            return driveStatus;
        }
        catch (Exception e)
        {
            m_Log.Error("GetElementStatus", e);
            throw;
        }
        finally
        {
            if (ptrIn != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(ptrIn);
            }
            if (ptrOut != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(ptrOut);
            }
            m_Log.Debug("EXIT GetElementStatus");
        }
    }

    public void Discover()
    {
        int errorCode = 0;

        Changer.ChangerElementStatusEx status;

        for(int i = 0; i++; i < int.MaxValue)
        {
            try
            {
                status = GetElementStatus((uint)i, Changer.ElementType.ChangerSlot);
                m_Log.InfoFormat("Regular slot {0} is {1} empty", 
				i, status.IsEmpty ? "" : "not");
            }
            catch (Win32Exception e)
            {
                if (e.NativeErrorCode == 
		ERROR_ILLEGAL_ELEMENT_ADDRESS) break; //no more elements

                throw;
            }
        }

        status = m_TapeOperator.GetElementStatus(0, Changer.ElementType.ChangerDrive);
        m_Log.InfoFormat("Driver is {0} empty", status.IsEmpty ? "" : "not");
    } 

Conclusion

This article covered Status functionality of Media Changers. When status of Media Changer is obtained, we can perform MOVE, EJECT, INSERT and IO functionality to perform pretty good backup system.

For IO functionality, you can see Magnetic tape data storage. Part 1.

MOVE, EJECT and INSERT will be covered in the next article.

History

  • 28th October, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
Israel Israel
Name: Statz Dima
Fields of interest: software

Comments and Discussions

 
QuestionTape Library inventorying Pin
Murali Mano12-Feb-17 22:53
Murali Mano12-Feb-17 22:53 
Can some one help me to inventory a tape library and map the bar-code of a tape with the slot on which it is inserted in a tape library through c++ or c#.

I am trying to develop a backup system with tape media. I did some code works in c++ to write and read my data through tape. I used windows function to perform read write functions in tape.

Reference : https://msdn.microsoft.com/en-us/library/windows/desktop/aa362558(v=vs.85).aspx

Everything works fine. The rest is managing the tapes. I googled and found windows functions ( DeviceIoControl()), through which i could able to mount a tape media into the drive. The problem i face is that i have a number of slots in a tape library and tape media can be inserted in to each slots and each tape media has a defined barcode with it and i was not able to find how to get the barcode and slot relation through controlling the robotic arm. Without knowing the barcode one can easily put a tape media from a slot to drive for writing and reading. But say in a library like environment with a backup setup i need to find the barcode to fetch it whenever i need it for restore.

I would like to know is there any windows functions like deviceioctl or through powershell or through any other method which we could control the robotic arm in a tape library to read and find the barcode and slot relation.

For Tape library video reference:

Teardown: IBM LTO-3 Tape Drive & Dell TL-2000 Tape Library

https://www.youtube.com/watch?v=x3lseFxGfXM

modified 13-Feb-17 5:54am.

QuestionGreat Article Pin
Member 39272021-Aug-13 9:08
Member 39272021-Aug-13 9:08 
QuestionError 5 Pin
String Sharp26-May-12 3:49
String Sharp26-May-12 3:49 
Questionwaiting for the third part Pin
amini_porroo30-Apr-12 20:56
amini_porroo30-Apr-12 20:56 
QuestionTape IO with media changer Pin
billl papan12-Mar-12 4:36
billl papan12-Mar-12 4:36 
Questionno Changer driver therfore no changer "device", what then? Pin
Member_263752619-Oct-11 12:53
Member_263752619-Oct-11 12:53 
AnswerRe: no Changer driver therfore no changer "device", what then? Pin
Dima Statz20-Oct-11 7:34
Dima Statz20-Oct-11 7:34 
QuestionCode? Pin
fssd16-Feb-11 10:43
fssd16-Feb-11 10:43 
AnswerRe: Code? [modified] Pin
Dima Statz22-Feb-11 22:09
Dima Statz22-Feb-11 22:09 
GeneralRe: Code? [modified] Pin
String Sharp16-Jun-12 22:31
String Sharp16-Jun-12 22:31 
GeneralRe: Code? [modified] Pin
BillyDvd29-Jun-12 6:21
BillyDvd29-Jun-12 6:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.