DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_ioctl.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include "dosbox.h"
00022 #include "callback.h"
00023 #include "mem.h"
00024 #include "regs.h"
00025 #include "dos_inc.h"
00026 
00027 bool DOS_IOCTL(void) {
00028         Bitu handle=0;Bit8u drive=0;
00029         /* calls 0-4,6,7,10,12,16 use a file handle */
00030         if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) {
00031                 handle=RealHandle(reg_bx);
00032                 if (handle>=DOS_FILES) {
00033                         DOS_SetError(DOSERR_INVALID_HANDLE);
00034                         return false;
00035                 }
00036                 if (!Files[handle]) {
00037                         DOS_SetError(DOSERR_INVALID_HANDLE);
00038                         return false;
00039                 }
00040         } else if (reg_al<0x12) {                               /* those use a diskdrive except 0x0b */
00041                 if (reg_al!=0x0b) {
00042                         drive=reg_bl;if (!drive) drive = DOS_GetDefaultDrive();else drive--;
00043                         if( (drive >= 2) && !(( drive < DOS_DRIVES ) && Drives[drive]) ) {
00044                                 DOS_SetError(DOSERR_INVALID_DRIVE);
00045                                 return false;
00046                         }
00047                 }
00048         } else {
00049                 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
00050                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00051                 return false;
00052         }
00053         switch(reg_al) {
00054         case 0x00:              /* Get Device Information */
00055                 if (Files[handle]->GetInformation() & 0x8000) { //Check for device
00056                         reg_dx=Files[handle]->GetInformation();
00057                 } else {
00058                         Bit8u hdrive=Files[handle]->GetDrive();
00059                         if (hdrive==0xff) {
00060                                 LOG(LOG_IOCTL,LOG_NORMAL)("00:No drive set");
00061                                 hdrive=2;       // defaulting to C:
00062                         }
00063                         /* return drive number in lower 5 bits for block devices */
00064                         reg_dx=(Files[handle]->GetInformation()&0xffe0)|hdrive;
00065                 }
00066                 reg_ax=reg_dx; //Destroyed officially
00067                 return true;
00068         case 0x01:              /* Set Device Information */
00069                 if (reg_dh != 0) {
00070                         DOS_SetError(DOSERR_DATA_INVALID);
00071                         return false;
00072                 } else {
00073                         if (Files[handle]->GetInformation() & 0x8000) { //Check for device
00074                                 reg_al=(Bit8u)(Files[handle]->GetInformation() & 0xff);
00075                         } else {
00076                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00077                                 return false;
00078                         }
00079                 }
00080                 return true;
00081         case 0x02:              /* Read from Device Control Channel */
00082                 if (Files[handle]->GetInformation() & 0xc000) {
00083                         /* is character device with IOCTL support */
00084                         PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
00085                         Bit16u retcode=0;
00086                         if (((DOS_Device*)(Files[handle]))->ReadFromControlChannel(bufptr,reg_cx,&retcode)) {
00087                                 reg_ax=retcode;
00088                                 return true;
00089                         }
00090                 }
00091                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00092                 return false;
00093         case 0x03:              /* Write to Device Control Channel */
00094                 if (Files[handle]->GetInformation() & 0xc000) {
00095                         /* is character device with IOCTL support */
00096                         PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
00097                         Bit16u retcode=0;
00098                         if (((DOS_Device*)(Files[handle]))->WriteToControlChannel(bufptr,reg_cx,&retcode)) {
00099                                 reg_ax=retcode;
00100                                 return true;
00101                         }
00102                 }
00103                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00104                 return false;
00105         case 0x06:      /* Get Input Status */
00106                 if (Files[handle]->GetInformation() & 0x8000) {         //Check for device
00107                         reg_al=(Files[handle]->GetInformation() & 0x40) ? 0x0 : 0xff;
00108                 } else { // FILE
00109                         Bit32u oldlocation=0;
00110                         Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR);
00111                         Bit32u endlocation=0;
00112                         Files[handle]->Seek(&endlocation, DOS_SEEK_END);
00113                         if(oldlocation < endlocation){//Still data available
00114                                 reg_al=0xff;
00115                         } else {
00116                                 reg_al=0x0; //EOF or beyond
00117                         }
00118                         Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); //restore filelocation
00119                         LOG(LOG_IOCTL,LOG_NORMAL)("06:Used Get Input Status on regular file with handle %d",(int)handle);
00120                 }
00121                 return true;
00122         case 0x07:              /* Get Output Status */
00123                 LOG(LOG_IOCTL,LOG_NORMAL)("07:Fakes output status is ready for handle %d",(int)handle);
00124                 reg_al=0xff;
00125                 return true;
00126         case 0x08:              /* Check if block device removable */
00127                 /* cdrom drives and drive a&b are removable */
00128                 if (drive < 2) reg_ax=0;
00129                 else if (!Drives[drive]->isRemovable()) reg_ax=1;
00130                 else {
00131                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00132                         return false;
00133                 }
00134                 return true;
00135         case 0x09:              /* Check if block device remote */
00136                 if ((drive >= 2) && Drives[drive]->isRemote()) {
00137                         reg_dx=0x1000;  // device is remote
00138                         // undocumented bits always clear
00139                 } else {
00140                         reg_dx=0x0802;  // Open/Close supported; 32bit access supported (any use? fixes Fable installer)
00141                         // undocumented bits from device attribute word
00142                         // TODO Set bit 9 on drives that don't support direct I/O
00143                 }
00144                 reg_ax=0x300;
00145                 return true;
00146         case 0x0A:              /* Is Device of Handle Remote? */
00147                 reg_dx=0x8000;
00148                 LOG(LOG_IOCTL,LOG_NORMAL)("0A:Faked output: device of handle %d is remote",(int)handle);
00149                 return true;
00150         case 0x0B:              /* Set sharing retry count */
00151                 if (reg_dx==0) {
00152                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00153                         return false;
00154                 }
00155                 return true;
00156         case 0x0D:              /* Generic block device request */
00157                 {
00158                         if (drive < 2 && !Drives[drive]) {
00159                                 DOS_SetError(DOSERR_ACCESS_DENIED);
00160                                 return false;
00161                         }
00162                         if (reg_ch != 0x08 || Drives[drive]->isRemovable()) {
00163                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00164                                 return false;
00165                         }
00166                         PhysPt ptr      = SegPhys(ds)+reg_dx;
00167                         switch (reg_cl) {
00168                         case 0x60:              /* Get Device parameters */
00169                                 //mem_writeb(ptr+0,0);                                  // special functions (call value)
00170                                 mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
00171                                 mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
00172                                 mem_writew(ptr+4,0x0000);                               // num of cylinders
00173                                 mem_writeb(ptr+6,0x00);                                 // media type (00=other type)
00174                                 // bios parameter block following
00175                                 mem_writew(ptr+7,0x0200);                               // bytes per sector (Win3 File Mgr. uses it)
00176                                 break;
00177                         case 0x46:      /* Set volume serial number */
00178                                 break;
00179                         case 0x66:      /* Get volume serial number */
00180                                 {                       
00181                                         char const* bufin=Drives[drive]->GetLabel();
00182                                         char buffer[11];memset(buffer,' ',11);
00183 
00184                                         char const* find_ext=strchr(bufin,'.');
00185                                         if (find_ext) {
00186                                                 Bitu size=(Bitu)(find_ext-bufin);
00187                                                 if (size>8) size=8;
00188                                                 memcpy(buffer,bufin,size);
00189                                                 find_ext++;
00190                                                 memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); 
00191                                         } else {
00192                                                 memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
00193                                         }
00194                         
00195                                         char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
00196                                         if(drive<2) buf2[4] = '2'; //FAT12 for floppies
00197 
00198                                         //mem_writew(ptr+0,0);                  //Info level (call value)
00199                                         mem_writed(ptr+2,0x1234);               //Serial number
00200                                         MEM_BlockWrite(ptr+6,buffer,11);//volumename
00201                                         MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem
00202                                 }
00203                                 break;
00204                         default :       
00205                                 LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive);
00206                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00207                                 return false;
00208                         }
00209                         reg_ax=0;
00210                         return true;
00211                 }
00212         case 0x0E:                      /* Get Logical Drive Map */
00213                 if (drive < 2) {
00214                         if (Drives[drive]) reg_al=drive+1;
00215                         else reg_al=1;
00216                 } else if (Drives[drive]->isRemovable()) {
00217                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00218                         return false;
00219                 } else reg_al=0;        /* Only 1 logical drive assigned */
00220                 reg_ah=0x07;
00221                 return true;
00222         default:
00223                 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
00224                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00225                 break;
00226         }
00227         return false;
00228 }
00229 
00230 
00231 bool DOS_GetSTDINStatus(void) {
00232         Bit32u handle=RealHandle(STDIN);
00233         if (handle==0xFF) return false;
00234         if (Files[handle] && (Files[handle]->GetInformation() & 64)) return false;
00235         return true;
00236 }