/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = CDIOCTL.C
 *
 * DESCRIPTIVE NAME = IOCTL handling routines for OS/2 CD-ROM Device Mgr
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "cdh.h"
#include "ioctl.h"
#include "dskioctl.h"

typedef struct _IOCTL_TABLE_ENTRY
{
   USHORT Function;
   USHORT (NEAR *pIOCTL_Routine)(PRP_GENIOCTL, NPUNITCB);
   UCHAR  ParamPktLen;
   UCHAR  DataPktLen;
} IOCTL_TABLE_ENTRY;


typedef struct _LOCKSTRUC
{
  USHORT flags;
  ULONG  lin_lock_struc;
  UCHAR  lockhandle_param[12];
  UCHAR  lockhandle_data[12];
} LOCKSTRUC, FAR * PLOCKSTRUC;


#define PARM_PACKET_LOCKED   1
#define DATA_PACKET_LOCKED   2


/****************************************************************************
 *
 * FUNCTION NAME = f_CD_DriveGenIOCTL
 *
 * DESCRIPTION   = Category 8/80/81 IOCTL routines
 *
 *          Category 8/80/81 IOCTL routines and IOCTL function router.
 *
 *          USHORT f_CD_DriveGenIOCTL (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR f_CD_DriveGenIOCTL (pRP, pUnitCB)

PRP_GENIOCTL  pRP;
NPUNITCB      pUnitCB;

{
   /*
   ** IOCTL Command Router Tables
   */

   /*
   ** Category 8 - Logical Disk Control Command Router Table
   */
    static IOCTL_TABLE_ENTRY Cat8_IOCTL_Table[] =
    {
     {IODC_GP,            CD_GetDeviceParms8,
                          sizeof(DDI_DeviceParameters_param),
                          sizeof(DDI_DeviceParameters_data)},

     {IODC_RC,            CD_RemovableMediaControl,
                          sizeof(DDI_DsktRemovMediaCtl_param),
                          0},

     {IODC_ST,            CD_RemovableDeviceStatus,
                          0,
                          sizeof(DDI_DsktRemovMediaCtl_data)},

     {-1},                // End of Table
    };

    static IOCTL_TABLE_ENTRY Cat9_IOCTL_Table[] =
    {
     {IODC_GP,            CD_GetDeviceParms9,
                          sizeof(DDI_DeviceParameters_param),
                          sizeof(DDI_PhysDeviceParameters_data)},

     {-1},                // End of Table
    };

    /*
    ** Category 80 - CD-ROM IOCTL Command Router Table
    */
    static IOCTL_TABLE_ENTRY Cat80_IOCTL_Table[] =
    {
     {IOCD_RESET,         CD_ResetDrive,
                          sizeof(struct ResetDrive),
                          0},

     {IOCD_EJECT,         CD_EjectDisk,
                          sizeof(struct EjectDisk),
                          0},

     {IOCD_CLOSE_TRAY,    CD_CloseTray,
                          sizeof(struct CloseTray),
                          0},

     {IOCD_LOCK_UNLOCK,   CD_LockUnlock,
                          sizeof(struct LockUnlock),
                          0},

/*   {IOCD_WRITE_CTRL,    CD_WriteCtrl},  */

     {IOCD_SEEK,          CD_Seek,
                          sizeof(struct Seek),
                          0},


     {IOCD_WRITE_LONG,    CD_WriteLong,
                          sizeof(struct WriteLong),
                          1},

     {IOCD_WRITEV_LONG,   CD_WriteVLong,
                          sizeof(struct WriteLong),
                          1},

     {IOCD_DEVICE_STATUS, CD_DeviceStatus,
                          sizeof(struct DeviceStatus),
                          sizeof(struct DeviceStatus_Data)},

     {IOCD_IDENTIFY,      CD_Identify,
                          sizeof(struct IdentifyCDROMdriver),
                          sizeof(struct IdentifyCDROMdriver_Data)},

     {IOCD_SECTOR_SIZE,   CD_ReturnSectorSize,
                          sizeof(struct ReturnSectorSize),
                          sizeof(struct ReturnSectorSize_Data)},

/*   {IOCD_READ_CTRL,     CD_ReadCtrl},   */

     {IOCD_HEAD_LOCATION, CD_HeadLocation,
                          sizeof(struct LocationOfHead),
                          sizeof(struct LocationOfHead_Data)},

     {IOCD_READ_PREFETCH, CD_ReadPrefetch,
                          sizeof(struct ReadPrefetch),
                          0},

     {IOCD_READ_LONG,     CD_ReadLong,
                          sizeof(struct ReadLong),
                          1},

     {IOCD_READ_LONG_PRE, CD_ReadLongPre,
                          sizeof(struct ReadLongPrefetch),
                          0},

     {IOCD_VOLUME_SIZE,   CD_ReturnVolumeSize,
                          sizeof(struct ReturnVolumeSize),
                          sizeof(struct ReturnVolumeSize_Data)},

     {IOCD_UPC,           CD_GetUPC,
                          sizeof(struct UPCCode),
                          sizeof(struct UPCCode_Data)},

     {-1},                // End of Table
    };

 /*
 ** Category 81H - CD-ROM AUDIO IOCTL Command Router Table
 */
    static IOCTL_TABLE_ENTRY Cat81_IOCTL_Table[] =
    {
     {IOCD_CHANNEL_CTRL,    CD_AudioChannelCtrl,
                            sizeof(struct AudioChannelControl),
                            sizeof(struct AudioChannelControl_Data)},

     {IOCD_PLAY,            CD_Play,
                            sizeof(struct PlayAudio),
                            0},

     {IOCD_STOP,            CD_Stop,
                            sizeof(struct StopAudio),
                            0},


     {IOCD_RESUME,          CD_Resume,
                            sizeof(struct ResumeAudio),
                            0},

     {IOCD_CHANNEL_INFO,    CD_AudioChannelInfo,
                            sizeof(struct AudioChannelInfo),
                            sizeof(struct AudioChannelInfo_Data)},

     {IOCD_DISK_INFO,       CD_AudioDiskInfo,
                            sizeof(struct AudioDiskInfo),
                            sizeof(struct AudioDiskInfo_Data)},

     {IOCD_TRACK_INFO,      CD_AudioTrackInfo,
                            sizeof(struct AudioTrackInfo),
                            sizeof(struct AudioTrackInfo_Data)},

     {IOCD_QCHANNEL_INFO,   CD_AudioQChannelInfo,
                            sizeof(struct AudioQChannelInfo),
                            sizeof(struct AudioQChannelInfo_Data)},

     {IOCD_SUBCHANNEL_INFO, CD_AudioSubChannelInfo,
                            sizeof(struct AudioSubChannelInfo),
                            -1},

     {IOCD_STATUS_INFO,     CD_AudioStatusInfo,
                            sizeof(struct AudioStatusInfo),
                            sizeof(struct AudioStatusInfo_Data)},

     {-1},                  // End of Table
    };


   /*
   ** Verify it's an IOCTL the driver supports, verify
   ** the parameter and data packets, and route the
   ** the request to it's command handler.
   */

   USHORT  rc, Unit, i;
   USHORT (NEAR *pIOCTL_Routine)(PRP_GENIOCTL, NPUNITCB);
   IOCTL_TABLE_ENTRY *pTable;

   LOCKSTRUC lock_struc;

   switch (pRP->Category)
   {
      case IOC_DC:
         pTable = Cat8_IOCTL_Table;
         break;

      case IOC_PD:
         pTable = Cat9_IOCTL_Table;
         break;

      case IOC_CDROM:
         pTable = Cat80_IOCTL_Table;
         break;

      case IOC_CDROM_AUDIO:
         pTable = Cat81_IOCTL_Table;
         break;

      default:
         return (STERR + STDON + ERROR_I24_BAD_COMMAND);
   }

   /*
   ** Verify it's an IOCTL Function the  driver supports
   ** and get the entry point of the IOCTL function to call
   */
   for (i = 0; pTable[i].Function != -1; i++)
   {
      if (pRP->Function == pTable[i].Function)
      {
         pIOCTL_Routine = pTable[i].pIOCTL_Routine;
         break;
      }
   }

   if (pTable[i].Function == -1)
      return (STERR + STDON + ERROR_I24_BAD_COMMAND);

   /*
   ** Valid Category and Command Code.  Now validate access to the
   ** parameter and data packets.  Lock down the data packet if required.
   */
   Unit = pRP->rph.Unit;

   lock_struc.flags = 0;

   if (VerifyParameters(pRP, pTable[i].ParamPktLen,
                             pTable[i].DataPktLen,
                             (LOCKSTRUC FAR *) &lock_struc) == STDON)

      /*
      ** Invoke the command handler for the IOCTL
      */
   {
      rc = (*pIOCTL_Routine)(pRP, pUnitCB);
   }
   else
   {
      rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   }


   if (lock_struc.flags & PARM_PACKET_LOCKED)
   {
         DevHelp_VMUnLock (lock_struc.lin_lock_struc +
                          (ULONG)((USHORT) &(lock_struc.lockhandle_param[0]) -
                                  (USHORT) &(lock_struc)));
   }

   if (lock_struc.flags & DATA_PACKET_LOCKED)
   {
         DevHelp_VMUnLock (lock_struc.lin_lock_struc +
                          (ULONG)((USHORT) &(lock_struc.lockhandle_data[0]) -
                                  (USHORT) &(lock_struc)));
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = VerifyParameters
 *
 * DESCRIPTION   = Verify Parameter and Data Packet Pointers
 *
 *       Verify access to the Parameter and Data Packet pointer is the
 *       IOCTL request packet.  If the request is for an operation where
 *       data will be DMA'ed directly into the data packet, then issue a
 *       a long term lock on the data packet.
 *
 *       USHORT VerifyParameters (PRP_GENIOCTL pRP, USHORT ParmPktLen,
 *                                                  USHORT DataPktLen,
 *                                                  PBYTE plock_struc,
 *
 * INPUT         = pRP              - Request Packet
 *                 ParamPktLen      - Length of Parameter Packet
 *                 DataPktLen       - Length of Data Packet
 *                 plock_struc      - address of lock structure
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT VerifyParameters (pRP, ParamPktLen, DataPktLen, plock_struc)

PRP_GENIOCTL pRP;
USHORT       ParamPktLen;
USHORT       DataPktLen;
PLOCKSTRUC   plock_struc;

{
   USHORT rc;
   ULONG lin_Pkt;                /* Linear address of Parm or Data Packet */
   ULONG lin_lock_struc;
   ULONG lin_lockhandle_param;
   ULONG lin_lockhandle_data;
/* ULONG lin_page_list;                                                   */
   ULONG PageListCount;
   ULONG Length;
   ULONG VMLockFlags;


   rc = 0;

   /*
   ** Verify the string CD01 appears at the beginning of each Cat 80
   ** and Cat 81 parameter packet.
   */
   if (pRP->Category == IOC_CDROM || pRP->Category == IOC_CDROM_AUDIO)
   {
      if ( (pRP->Category == IOC_CDROM) && (pRP->Function == IOCD_IDENTIFY) )
      ;
      else
        if ( ((struct ResetDrive FAR *)pRP->ParmPacket)->ID_code != CD01 )
            return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);
   }

   /*
   ** Get linear address of lock structure on the stack
   */
   DevHelp_VirtToLin(SELECTOROF(plock_struc),
                    (ULONG) OFFSETOF(plock_struc),
                    (PVOID) &lin_lock_struc);

   plock_struc->lin_lock_struc = lin_lock_struc;

/*
** lin_pagelist =  lin_lock_struc +
**                 (ULONG) ((USHORT) &(plock_struc->page_list[0]) -
**                          (USHORT) &(plock_struc));
*/
   /*
   ** Verify access to the parameter packet
   */
   if (ParamPktLen != 0)
   {
      /*
      ** Get linear address of the Parameter Packet
      */
      rc = DevHelp_VirtToLin(SELECTOROF(pRP->ParmPacket),
                            (ULONG) OFFSETOF(pRP->ParmPacket),
                            (PVOID) &lin_Pkt);
      if (rc == 0)
      {
         lin_lockhandle_param = lin_lock_struc +
                       (ULONG) ((USHORT) &(plock_struc->lockhandle_param[0]) -
                                (USHORT) &(plock_struc->flags));

         if ((rc = DevHelp_VMLock(VMDHL_LONG | VMDHL_VERIFY,
                                  lin_Pkt,
                                  ParamPktLen,
                                  -1L,
                                  lin_lockhandle_param,
                                  (PULONG) &PageListCount)) == 0)
         {
            plock_struc->flags = PARM_PACKET_LOCKED;
         }
      }
   }

   /*
   ** Verify access to the data packet.  If it's a ReadLong, then
   ** long term lock and make contig the data packet.
   */
   if (rc == 0 && DataPktLen != 0)
   {
      rc = DevHelp_VirtToLin(SELECTOROF(pRP->DataPacket),
                       (ULONG) OFFSETOF(pRP->DataPacket),
                       (PVOID) &lin_Pkt);
      if (rc == 0)
      {
         lin_lockhandle_data = lin_lock_struc +
                       (ULONG) ((USHORT) &(plock_struc->lockhandle_data[0]) -
                                (USHORT) &(plock_struc->flags));

         Length = DataPktLen;
         VMLockFlags = VMDHL_WRITE | VMDHL_LONG | VMDHL_VERIFY;

         if ((pRP->Category == IOC_CDROM) && (pRP->Function == IOCD_READ_LONG))
         {
            VMLockFlags = VMDHL_CONTIGUOUS | VMDHL_16M |
                          VMDHL_WRITE | VMDHL_LONG;

            Length = ((struct ReadLong FAR *)pRP->ParmPacket)->transfer_count
                                                      * 2352;
         }

         rc = DevHelp_VMLock(VMLockFlags, lin_Pkt, Length, -1L,
                     lin_lockhandle_data, (PULONG) &PageListCount);
      }

      if (rc == 0)
      {
         plock_struc->flags |= DATA_PACKET_LOCKED;
      }
      else
      {
         if (plock_struc->flags & PARM_PACKET_LOCKED)
         {
            DevHelp_VMUnLock(lin_lockhandle_param);
            plock_struc->flags = 0;
         }
      }
   }

   if (rc)
      rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   else
      rc = STDON;

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_GetDeviceParms8
 *
 * DESCRIPTION   = Get Device Parms
 *
 *       Transfers the BPB, Number of Cylinders, Device Type and Device
 *       attributes to the IOCTL data packet.
 *
 *       USHORT CD_GetDeviceParms8(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_GetDeviceParms8 (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   PDDI_DeviceParameters_param pParmPkt;
   PDDI_DeviceParameters_data pDataPkt;
   USHORT rc;
   ULONG volume_size;

   pParmPkt = (PDDI_DeviceParameters_param) pRP->ParmPacket;
   pDataPkt = (PDDI_DeviceParameters_data) pRP->DataPacket;

   if (pParmPkt->Command & ~COMMAND_INFO_MASK)
      return(STDON + STERR + ERROR_I24_INVALID_PARAMETER);

   rc = GetVolumeSize (pUnitCB, (ULONG FAR *) &volume_size);

   pDataPkt->bpb = DefaultBPB;

   if (rc == STDON)
      pDataPkt->bpb.BigTotalSectors = volume_size;
   else
      pDataPkt->bpb.BigTotalSectors = 270000L;


   pDataPkt->NumCylinders = DEFAULT_CYLINDER_COUNT;
   pDataPkt->DeviceType = TYPE_CDROM;

   pDataPkt->DeviceAttr = 0;

   if ( !(pUnitCB->UnitInfo.UnitFlags & UF_REMOVABLE) )
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_NON_REMOVABLE;

/* if (pUnitCB->UnitInfo.UnitFlags & UF_CHANGELINE)                        */
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_CHANGELINE;

   if (pUnitCB->Flags & UCF_16M)
      pDataPkt->DeviceAttr |= DP_DEVICEATTR_GT16MBSUPPORT;


   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_GetDeviceParms9
 *
 * DESCRIPTION   = Cat 9 Get Device Parms
 *
 *           Transfer physical device paramaters
 *
 *           USHORT CD_GetDeviceParms9(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_GetDeviceParms9 (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   PDDI_PhysDeviceParameters_data pDataPkt;

   pDataPkt = (PDDI_PhysDeviceParameters_data) pRP->DataPacket;

   pDataPkt->NumCylinders = DEFAULT_CYLINDER_COUNT;
   pDataPkt->NumHeads = 1;
   pDataPkt->SectorsPerTrack = 0xFFFF;

   return(STDON);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_RemovableMediaControl
 *
 * DESCRIPTION   = Removable Media Control
 *
 *      Cat 8 function to allow lock, unlock or eject of media in the drive.
 *      This function simply calls the equivalent Cat 80 function.
 *
 *      USHORT CD_RemovableMediaControl(PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_RemovableMediaControl (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   USHORT rc;
   PDDI_DsktRemovMediaCtl_param pParmPkt;

   pParmPkt = (PDDI_DsktRemovMediaCtl_param) pRP->ParmPacket;

   switch (pParmPkt->Command)
   {
      case UNLOCK_MEDIA:
      case LOCK_MEDIA:
        rc = CD_LockUnlock(pRP, pUnitCB);
        break;

      case EJECT_MEDIA:
        rc = CD_EjectDisk(pRP, pUnitCB);
        break;

      default:
         rc = STDON + STERR + ERROR_I24_INVALID_PARAMETER;
   }

   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = CD_RemovableDeviceStatus
 *
 * DESCRIPTION   = return device status
 *
 *        Cat 8 function to return removable media status
 *
 *        USHORT CD_RemovableDeviceStatus (PRP_GENIOCTL pRP, NPUNITCB pUnitCB)
 *
 * INPUT         = pRP              - Request Packet
 *                 pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = USHORT           - Packet Status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT CD_RemovableDeviceStatus (pRP, pUnitCB)

PRP_GENIOCTL pRP;
NPUNITCB     pUnitCB;

{
   NPIORB_CDB pIORB;
   ULONG status, capabilities;                                       /*V@91985*/
   PDDI_DsktRemovMediaCtl_data pDataPkt;
   struct CapabilitiesParmList_10 NEAR *pCDBData;                    /*V@91985*/
   struct CDB_ModeSense_10  NEAR *pCDB;                              /*V@91985*/

   pDataPkt = (PDDI_DsktRemovMediaCtl_data) pRP->DataPacket;

   if (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)        /*V@91985*/
   {                                                                 /*V@91985*/
       /*                                                              V@91985
       ** Issue Mode Sense to read the CD-ROM capabilites page.        V@91985
       */                                                            /*V@91985*/
                                                                     /*V@91985*/
       BuildCDB_SenseCapabilities (pUnitCB,                          /*V@91985*/
                                   (NPIORB_CDB FAR *) &pIORB);       /*V@91985*/
       pCDB = (struct CDB_ModeSense_10 NEAR *) &(pIORB->CDB);        /*V@91985*/
       pCDB->PC = PC_CURRENT;                                        /*V@91985*/
                                                                     /*V@91985*/
       SubmitIORB_Wait (pUnitCB, pIORB);                             /*V@91985*/
                                                                     /*V@91985*/
       if ( !(pIORB->apt.iorbh.Status & IORB_ERROR) )                /*V@91985*/
       {                                                             /*V@91985*/
          pCDBData = (struct CapabilitiesParmList_10 NEAR *)         /*V@91985*/
                                            pIORB->CDB_data;         /*V@91985*/
          capabilities = pCDBData->CapPage.capabilities;             /*V@91985*/
                                                                     /*V@91985*/
          if (capabilities & CP_LOCK_STATE )                         /*V@91985*/
             status = 1;     /* Drive Locked, lock supported */      /*V@91985*/
          else                                                       /*V@91985*/
             status = 2;     /* Drive Unlocked, lock supported */    /*V@91985*/
       }                                                             /*V@91985*/
       else                                                          /*V@91985*/
          status = 0;        /* Mode sense failed.            */     /*V@91985*/
                             /* Assume no lock/unlock support */     /*V@91985*/
       FreeIORB (pUnitCB, pIORB);                                    /*V@91985*/
   }                                                                 /*V@91985*/
   else                                                              /*V@91985*/
      status = 3;                    /* Lock supported, but lock status isnt */

   BuildCDB_TestUnitReady (pUnitCB, (NPIORB_CDB FAR *) &pIORB);

   if  (SubmitIORB_Wait (pUnitCB, pIORB) == STDON)
      status |= MEDIA_IN_DRIVE;

   FreeIORB (pUnitCB, pIORB);

   pDataPkt->Status = status;

   return(STDON);
}
