home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / libc / compat / ioctl / ioctl.c next >
Encoding:
C/C++ Source or Header  |  1996-09-01  |  11.2 KB  |  379 lines

  1. /*
  2. ** File    : /djgpp/src/libc/posix/fcntl/ioctl.c
  3. ** Author  : Tom Demmer (demmer@lstm.ruhr-uni-bochum.de)
  4. ** SYNOPSIS: ioctl() for djgpp.
  5. ** Aim:
  6. **   Provide an ioctl function that does what the naive DOS C programmer
  7. **   expects.
  8. **   Provide an ioctl function that does what the naive UNIX C programmer
  9. **   expects.
  10. **   Mangle and shredder them, roll them into one function and expect it
  11. **   to work both ways.
  12. **   If you really do that, you _ARE_ naive.
  13. **
  14. **   Generally, we follow this approach:
  15. **   Commands for ioctl are 8 bit long because they must fit into AL.
  16. **   The other 8 bit are usually unused. We put some flags inside, e.g.
  17. **   if we need a transfer buffer, which register the DOS ioctl call wants
  18. **   to return on saturdays and sun shine and such. The low byte of the
  19. **   high word takes some more flags.
  20. **
  21. **
  22. **
  23. **   The UNIX ioctls have the command in the lower word (16-Bit).
  24. **   In the high word, the upper three bits tell us if the parameters
  25. **   are in or out parameters. Bit 15 means IN, Bit 14 means OUT, Bit
  26. **   13 means VOID. Because there _must_ be one of them set, we can
  27. **   distinguish UNIX from DOS ioctls. The other tell us the size of the
  28. **   Input/Output parameters.
  29. **   The high byte of the lower word will be used to specify the class
  30. **   to which the command belongs, e.g. `T' for tty. Currently not
  31. **   implemented.
  32. **
  33. **
  34. **   It looks like this:
  35. **   DOS:
  36. **   000fffff ffffffff XFFiibRR CCCCCCCC
  37. **   UNIX:
  38. **   IOVxxxxx xSSSSSSS CCCCCCCC CCCCCCCC
  39. **
  40. **   With
  41. **      C: Command part
  42. **      X: Use Xfer buffer
  43. **      F: What to return
  44. **      f: Still free. Will be used for the generioc ioctl request.
  45. **      i: Number of additional input args (dx,si,di)
  46. **         I think CX is always used or can be safely set to 0
  47. **      b: Brain Dead Flag for generic ioctl
  48. **      R: reserved (might become the number of args someday)
  49. **
  50. **      S: sizeof input/output parameter
  51. **      I: Input  flag
  52. **      O: Output flag
  53. **      V: Void Flag (no parameters)
  54. **      x: Currently unused.
  55. **
  56. **   Right now the function supports the DOS ioctl calls up to 0x440b.
  57. **   If you want to add another function, it goes like this:
  58. **   #define DOS_PLAIN_your_func  0xff // e.g
  59. **   #define DOS_your_func (DOS_PLAIN_your_func | \
  60. **                          [DOS_XFER]| \
  61. **                          [DOS_RETAX/DOS_RETDX/DOS_RETDISI] |\
  62. **                          [DOS_XINARGS(xtra-args)])
  63. **
  64. **
  65. ** What is completely missing is the UNIX ioctl handler. I don't have the
  66. ** slightest idea about what to do here. Right now it just checks for an FSE
  67. ** and calls that if it exist. Otherwise we just return -1.
  68. **
  69. **
  70. $Id: ioctl.c 0.5 1996/07/30 09:42:23 DEMMER Exp DEMMER $
  71. $Log: ioctl.c $
  72.  * Revision 0.5  1996/07/30  09:42:23  DEMMER
  73.  * Minor code cleanups. Final beta.
  74.  *
  75.  * Revision 0.4  1996/07/29  13:03:29  DEMMER
  76.  * Added va_end(). Probably uneeded for Intel machines. Anyway.
  77.  *
  78.  * Revision 0.3  1996/07/29  12:44:55  DEMMER
  79.  * Split the header stuff from the source
  80.  * Changed encoding bits
  81.  *
  82.  * Revision 0.2  1996/07/04  11:17:37  DEMMER
  83.  * Revised flag scheme.
  84.  * Correct some minor bugs
  85.  *
  86.  * Revision 0.1  1996/07/03  15:42:01  DEMMER
  87.  * Cleaned up flag stuff.
  88.  * Added UNIX ioctl test routines
  89.  *
  90.  * Revision 0.0  1996/07/03  13:53:23  DEMMER
  91.  * Initial version
  92.  *
  93. */
  94.  
  95.  
  96.  
  97. #include <libc/stubs.h>
  98. #include <stdarg.h>
  99. #include <stdlib.h>
  100.  
  101. #include <sys/ioctl.h>
  102.  
  103.  
  104.       
  105. /****************************************************************************/
  106. /****************************************************************************/
  107. /* S T A R T   O F   I M P L E M E N T A T I O N  ***************************/
  108. /****************************************************************************/
  109. /****************************************************************************/
  110.  
  111.  
  112. #include <errno.h>
  113. #include <go32.h>
  114. #include <dpmi.h>
  115. #include <sys/fsext.h>
  116. #include <dos.h>
  117. #include <libc/dosio.h>
  118.  
  119. /*
  120. ** This one might be added to <go32.h>
  121. */
  122. #define __tb_size _go32_info_block.size_of_transfer_buffer
  123.  
  124. static int _dos_ioctl(int fd, int cmd, int argcx,int argdx,int argsi,int argdi,
  125.                       int xarg)
  126. {
  127. /*
  128. ** Do an int0x21,0x44xx and such
  129. */
  130.     __dpmi_regs r;
  131.     int dos_selector = 0;
  132.     int dos_segment = 0;
  133.  
  134.     /*
  135.     ** Lower word of cmd -> al
  136.     */
  137.     r.x.ax = 0x4400 |(cmd &0xff);
  138.     r.x.bx = fd;
  139.     r.x.cx = argcx;
  140.     r.x.dx = argdx;
  141.     r.x.si = argsi;
  142.     r.x.di = argdi;
  143.     /*
  144.     ** This one must have dl=0
  145.     */
  146.     if(cmd == DOS_SETDEVDATA) r.x.dx = argdx &0xf;
  147.     /*
  148.     ** Normally, there is a difference between reading and writing.
  149.     ** But some functions require codes in the buffer on input already.
  150.     ** So we transfer before int and after the int.
  151.     ** We always use DX as a pointer to the buffer.
  152.     ** I _do_ like clear APIs.
  153.     */
  154.     if(cmd & DOS_XFER){
  155.         if(argcx <= __tb_size){ /* Can we use transfer buffer ? */
  156.             dosmemput((void *)argdx,argcx, __tb);
  157.             r.x.ds = (__tb>>4) &0xffff;
  158.             r.x.dx = __tb &0xf;
  159.         }
  160.         else{
  161.             /* No, we have to get some DOS mem ourselves */
  162.             dos_segment = __dpmi_allocate_dos_memory((argcx+15)>>4,
  163.                                                      &dos_selector);
  164.             r.x.ds = dos_segment;
  165.             if(-1 == dos_segment){
  166.                 errno = ENOMEM;
  167.                 return -1;
  168.             }
  169.         }
  170.     }
  171.     /*
  172.     ** At DOS 3.2+ MS decided to change the API to an a bit more
  173.     ** abstruse way. CX now does not hold the number of bytes
  174.     ** to r/w, but is now a function_number:categegory_code.
  175.     ** The size of the XBuffer is determined by the device driver
  176.     ** which can set it to whatever it likes.
  177.     ** We do not have second sight, so you'll have to add this size as a
  178.     ** parameter after all the registers and the buffer pointer.
  179.     */
  180.     if( cmd & DOS_BRAINDEAD  ){
  181.         if(xarg <= __tb_size){ /* Can we use transfer buffer ? */
  182.             dosmemput((void *)argdx,xarg, __tb);
  183.             r.x.ds = (__tb>>4) &0xffff;
  184.             r.x.dx = __tb &0xf;
  185.         }
  186.         else{
  187.             /* No, we have to get some DOS mem ourselves */
  188.             dos_segment = __dpmi_allocate_dos_memory((xarg+15)>>4,
  189.                                                       &dos_selector);
  190.             r.x.ds = dos_segment;
  191.             if(-1 == dos_segment){
  192.                 errno = ENOMEM;
  193.                 return -1;
  194.             }
  195.         }
  196.     }
  197.     /*
  198.     ** Call DOS
  199.     */
  200.     if(-1 == __dpmi_int(0x21,&r)){
  201.         if(dos_selector) __dpmi_free_dos_memory(dos_selector);
  202.         errno = EINVAL;
  203.         return -1;
  204.     }
  205.     errno = 0 ;
  206.     /*
  207.     ** DOS error?
  208.     */
  209.     if(r.x.flags &1){
  210.         if(dos_selector) __dpmi_free_dos_memory(dos_selector);
  211.         errno = __doserr_to_errno(r.x.ax);
  212.         return -1;
  213.     }
  214.     /*
  215.     ** move back to our buffers and find the return value
  216.     */
  217.     if(cmd & DOS_XFER){
  218.         if(dos_selector){
  219.             dosmemget(dos_segment<<4,argcx,(void*) argdx);
  220.             __dpmi_free_dos_memory(dos_selector);
  221.         }
  222.         else
  223.            dosmemget(__tb,argcx,(void *) argdx);
  224.     }
  225.     if(cmd & DOS_BRAINDEAD){
  226.         if(dos_selector){
  227.             dosmemget(dos_segment<<4,xarg,(void*) argdx);
  228.             __dpmi_free_dos_memory(dos_selector);
  229.         }
  230.         else
  231.            dosmemget(__tb,xarg,(void *) argdx);
  232.     }
  233.     /*
  234.     ** Return the requested value or 0.
  235.     */
  236.     switch(cmd & DOS_RETMASK){
  237.         case DOS_RETAX   :   return r.x.ax;
  238.         case DOS_RETDX   :   return r.x.dx;
  239.         case DOS_RETDISI :   return (r.x.di << 16) | r.x.si;
  240.         default          :   return 0;
  241.     }
  242.     /* NOTREACHED */
  243. }
  244.  
  245.  
  246. static int _unix_ioctl(int fd,int cmd, int arg){
  247. /*
  248. ** What to do _HERE_ ?
  249. */
  250.     __FSEXT_Function *func = __FSEXT_get_function(fd);
  251.     if(func){
  252.         int rv;
  253.         if(func(__FSEXT_ioctl,&rv, &fd))
  254.            return rv;
  255.     }
  256.  
  257.     /*
  258.     ** All else fails so far.
  259.     */
  260.     errno =  ENOTTY;
  261.     return -1;
  262. }
  263.  
  264. /*
  265. **
  266. ** The user callable entry point.
  267. **
  268. */
  269. int ioctl(int fd, int cmd, ...){
  270.   va_list args;
  271.   int argcx,argdx,argsi,argdi;
  272.   int narg,xarg;
  273.   va_start(args,cmd);
  274.   
  275.   if(__IS_UNIX_IOCTL(cmd)){
  276.     int arg = va_arg(args, int);
  277.     va_end(args);
  278. #ifdef TEST
  279.     {
  280.     int inflg   = (cmd & IOC_IN)   == IOC_IN;
  281.     int outflg  = (cmd & IOC_OUT)  == IOC_OUT;
  282.     int voidflg = (cmd & IOC_VOID) == IOC_VOID;
  283.  
  284.     int size = (cmd >> 16) & IOCPARM_MASK;
  285.     char i_class = (cmd &0xff00) >> 8;
  286.     printf("Calling UNIX ioctl %x:\n"
  287.            "Class:\t\t'%c'\n"
  288.            "Inflag:\t\t%d\tOutflag:\t%d\tvoidflg:\t%d\n"
  289.            "Size:\t\t%d\n",
  290.            cmd & 0xffff,  i_class,
  291.            inflg,outflg,voidflg,size);
  292.     }
  293. #endif
  294.      return _unix_ioctl(fd,cmd,arg);
  295.   }
  296.   /* Handle a DOS request */
  297.   /* extract arguments */
  298.   narg = (cmd >> 12) & 3;
  299.   argdx=argsi=argdi=xarg=0;
  300.   argcx = va_arg(args,int);
  301.  
  302.   if (narg > 0)             argdx = va_arg(args,int);
  303.   if (narg > 1)             argdi = va_arg(args,int);
  304.   if (narg > 2)             argsi = va_arg(args,int);
  305.   if (cmd & DOS_BRAINDEAD)  xarg  = va_arg(args,int);
  306.   va_end(args);
  307.  
  308.   return _dos_ioctl(fd,cmd,argcx,argdx,argsi,argdi,xarg);
  309. }
  310.  
  311.  
  312.  
  313.  
  314. #ifdef TEST
  315.  
  316. #include <fcntl.h>
  317. #include <sys/stat.h>
  318. #include <unistd.h>
  319.  
  320. int main (int argc, char **argv){
  321.     int fd;
  322.     int res;
  323.     short *s;
  324.     char mybuf[512];
  325.  
  326.     if(-1== (fd=open("NUL",O_RDWR))){
  327.         return 2;
  328.     }
  329.     printf("fd: %d\n",fd);
  330.  
  331.     res = ioctl(fd,DOS_GETDEVDATA,0,0);
  332.     printf("nul:\tDEV_DATA: %x\n",res ) ;
  333.     res = ioctl(fileno(stdout),DOS_GETDEVDATA,0,0);
  334.     printf("stdout:\tDEV_DATA: %x\n",res ) ;
  335.     res = ioctl(fileno(stdin ),DOS_GETDEVDATA,0,0);
  336.     printf("stdin:\tDEV_DATA: %x\n",res ) ;
  337.     close(fd);
  338.     fd = open("ioctl.c",O_RDONLY);
  339.     res = ioctl(fd,DOS_GETDEVDATA,0,0);
  340.     printf("ioctl.c:\tDEV_DATA: %x\n",res ) ;
  341.     close(fd);
  342.     fd=open("EMMQXXX0",O_RDONLY);
  343.  
  344.     mybuf[0] = '\0';
  345.     mybuf[1] = '\0';
  346.     res = ioctl(fd,DOS_RCVDATA,6,&mybuf);
  347.     s = (short *) &mybuf;
  348.     if (*s == 0x25) printf("EMM386 > 4.45\n");
  349.     mybuf[0] = '\x02';
  350.     mybuf[1] = '\0';
  351.     res = ioctl(fd,DOS_RCVDATA,2,&mybuf);
  352.     printf("res: %d\tEMM Version %d.%d\n",res,(int )mybuf[0],(int) mybuf[1]);
  353.     close(fd);
  354.  
  355. /*
  356. **
  357. ** The generic ioctls need an extra parameter, because CX no longer
  358. ** holds the size for the buffer.
  359. **
  360. */
  361. /*  It would work like this:
  362.  
  363.     res=ioctl(fd,DOS_GENCHARREQ,category_code,&mybuf,driver_cmd,driver_cmd,
  364.               sizeof(buffer table));
  365.     R.Brown's interrupt list is not too specific about the role of SI and DI.
  366.     It is important for European DOS 4.0 and OS/2 comp box. He just says
  367.     they are the "parameter to pass to driver", whatever that might mean.
  368.     I guess you can safely set the to 0.
  369.  
  370. */
  371.  
  372.     printf("\n\nTIOCGETD = %x\n",TIOCGETD); ioctl(0,TIOCGETD,NULL);
  373.     printf("TIOCSETD = %x\n",TIOCSETD); ioctl(0,TIOCSETD,NULL);
  374.     printf("TIOCHPCL = %x\n",TIOCHPCL); ioctl(0,TIOCHPCL,NULL);
  375.     return 0;
  376. }
  377. #endif
  378.  
  379.