home *** CD-ROM | disk | FTP | other *** search
- /*
- ** File : /djgpp/src/libc/posix/fcntl/ioctl.c
- ** Author : Tom Demmer (demmer@lstm.ruhr-uni-bochum.de)
- ** SYNOPSIS: ioctl() for djgpp.
- ** Aim:
- ** Provide an ioctl function that does what the naive DOS C programmer
- ** expects.
- ** Provide an ioctl function that does what the naive UNIX C programmer
- ** expects.
- ** Mangle and shredder them, roll them into one function and expect it
- ** to work both ways.
- ** If you really do that, you _ARE_ naive.
- **
- ** Generally, we follow this approach:
- ** Commands for ioctl are 8 bit long because they must fit into AL.
- ** The other 8 bit are usually unused. We put some flags inside, e.g.
- ** if we need a transfer buffer, which register the DOS ioctl call wants
- ** to return on saturdays and sun shine and such. The low byte of the
- ** high word takes some more flags.
- **
- **
- **
- ** The UNIX ioctls have the command in the lower word (16-Bit).
- ** In the high word, the upper three bits tell us if the parameters
- ** are in or out parameters. Bit 15 means IN, Bit 14 means OUT, Bit
- ** 13 means VOID. Because there _must_ be one of them set, we can
- ** distinguish UNIX from DOS ioctls. The other tell us the size of the
- ** Input/Output parameters.
- ** The high byte of the lower word will be used to specify the class
- ** to which the command belongs, e.g. `T' for tty. Currently not
- ** implemented.
- **
- **
- ** It looks like this:
- ** DOS:
- ** 000fffff ffffffff XFFiibRR CCCCCCCC
- ** UNIX:
- ** IOVxxxxx xSSSSSSS CCCCCCCC CCCCCCCC
- **
- ** With
- ** C: Command part
- ** X: Use Xfer buffer
- ** F: What to return
- ** f: Still free. Will be used for the generioc ioctl request.
- ** i: Number of additional input args (dx,si,di)
- ** I think CX is always used or can be safely set to 0
- ** b: Brain Dead Flag for generic ioctl
- ** R: reserved (might become the number of args someday)
- **
- ** S: sizeof input/output parameter
- ** I: Input flag
- ** O: Output flag
- ** V: Void Flag (no parameters)
- ** x: Currently unused.
- **
- ** Right now the function supports the DOS ioctl calls up to 0x440b.
- ** If you want to add another function, it goes like this:
- ** #define DOS_PLAIN_your_func 0xff // e.g
- ** #define DOS_your_func (DOS_PLAIN_your_func | \
- ** [DOS_XFER]| \
- ** [DOS_RETAX/DOS_RETDX/DOS_RETDISI] |\
- ** [DOS_XINARGS(xtra-args)])
- **
- **
- ** What is completely missing is the UNIX ioctl handler. I don't have the
- ** slightest idea about what to do here. Right now it just checks for an FSE
- ** and calls that if it exist. Otherwise we just return -1.
- **
- **
- $Id: ioctl.c 0.5 1996/07/30 09:42:23 DEMMER Exp DEMMER $
- $Log: ioctl.c $
- * Revision 0.5 1996/07/30 09:42:23 DEMMER
- * Minor code cleanups. Final beta.
- *
- * Revision 0.4 1996/07/29 13:03:29 DEMMER
- * Added va_end(). Probably uneeded for Intel machines. Anyway.
- *
- * Revision 0.3 1996/07/29 12:44:55 DEMMER
- * Split the header stuff from the source
- * Changed encoding bits
- *
- * Revision 0.2 1996/07/04 11:17:37 DEMMER
- * Revised flag scheme.
- * Correct some minor bugs
- *
- * Revision 0.1 1996/07/03 15:42:01 DEMMER
- * Cleaned up flag stuff.
- * Added UNIX ioctl test routines
- *
- * Revision 0.0 1996/07/03 13:53:23 DEMMER
- * Initial version
- *
- */
-
-
-
- #include <libc/stubs.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- #include <sys/ioctl.h>
-
-
-
- /****************************************************************************/
- /****************************************************************************/
- /* S T A R T O F I M P L E M E N T A T I O N ***************************/
- /****************************************************************************/
- /****************************************************************************/
-
-
- #include <errno.h>
- #include <go32.h>
- #include <dpmi.h>
- #include <sys/fsext.h>
- #include <dos.h>
- #include <libc/dosio.h>
-
- /*
- ** This one might be added to <go32.h>
- */
- #define __tb_size _go32_info_block.size_of_transfer_buffer
-
- static int _dos_ioctl(int fd, int cmd, int argcx,int argdx,int argsi,int argdi,
- int xarg)
- {
- /*
- ** Do an int0x21,0x44xx and such
- */
- __dpmi_regs r;
- int dos_selector = 0;
- int dos_segment = 0;
-
- /*
- ** Lower word of cmd -> al
- */
- r.x.ax = 0x4400 |(cmd &0xff);
- r.x.bx = fd;
- r.x.cx = argcx;
- r.x.dx = argdx;
- r.x.si = argsi;
- r.x.di = argdi;
- /*
- ** This one must have dl=0
- */
- if(cmd == DOS_SETDEVDATA) r.x.dx = argdx &0xf;
- /*
- ** Normally, there is a difference between reading and writing.
- ** But some functions require codes in the buffer on input already.
- ** So we transfer before int and after the int.
- ** We always use DX as a pointer to the buffer.
- ** I _do_ like clear APIs.
- */
- if(cmd & DOS_XFER){
- if(argcx <= __tb_size){ /* Can we use transfer buffer ? */
- dosmemput((void *)argdx,argcx, __tb);
- r.x.ds = (__tb>>4) &0xffff;
- r.x.dx = __tb &0xf;
- }
- else{
- /* No, we have to get some DOS mem ourselves */
- dos_segment = __dpmi_allocate_dos_memory((argcx+15)>>4,
- &dos_selector);
- r.x.ds = dos_segment;
- if(-1 == dos_segment){
- errno = ENOMEM;
- return -1;
- }
- }
- }
- /*
- ** At DOS 3.2+ MS decided to change the API to an a bit more
- ** abstruse way. CX now does not hold the number of bytes
- ** to r/w, but is now a function_number:categegory_code.
- ** The size of the XBuffer is determined by the device driver
- ** which can set it to whatever it likes.
- ** We do not have second sight, so you'll have to add this size as a
- ** parameter after all the registers and the buffer pointer.
- */
- if( cmd & DOS_BRAINDEAD ){
- if(xarg <= __tb_size){ /* Can we use transfer buffer ? */
- dosmemput((void *)argdx,xarg, __tb);
- r.x.ds = (__tb>>4) &0xffff;
- r.x.dx = __tb &0xf;
- }
- else{
- /* No, we have to get some DOS mem ourselves */
- dos_segment = __dpmi_allocate_dos_memory((xarg+15)>>4,
- &dos_selector);
- r.x.ds = dos_segment;
- if(-1 == dos_segment){
- errno = ENOMEM;
- return -1;
- }
- }
- }
- /*
- ** Call DOS
- */
- if(-1 == __dpmi_int(0x21,&r)){
- if(dos_selector) __dpmi_free_dos_memory(dos_selector);
- errno = EINVAL;
- return -1;
- }
- errno = 0 ;
- /*
- ** DOS error?
- */
- if(r.x.flags &1){
- if(dos_selector) __dpmi_free_dos_memory(dos_selector);
- errno = __doserr_to_errno(r.x.ax);
- return -1;
- }
- /*
- ** move back to our buffers and find the return value
- */
- if(cmd & DOS_XFER){
- if(dos_selector){
- dosmemget(dos_segment<<4,argcx,(void*) argdx);
- __dpmi_free_dos_memory(dos_selector);
- }
- else
- dosmemget(__tb,argcx,(void *) argdx);
- }
- if(cmd & DOS_BRAINDEAD){
- if(dos_selector){
- dosmemget(dos_segment<<4,xarg,(void*) argdx);
- __dpmi_free_dos_memory(dos_selector);
- }
- else
- dosmemget(__tb,xarg,(void *) argdx);
- }
- /*
- ** Return the requested value or 0.
- */
- switch(cmd & DOS_RETMASK){
- case DOS_RETAX : return r.x.ax;
- case DOS_RETDX : return r.x.dx;
- case DOS_RETDISI : return (r.x.di << 16) | r.x.si;
- default : return 0;
- }
- /* NOTREACHED */
- }
-
-
- static int _unix_ioctl(int fd,int cmd, int arg){
- /*
- ** What to do _HERE_ ?
- */
- __FSEXT_Function *func = __FSEXT_get_function(fd);
- if(func){
- int rv;
- if(func(__FSEXT_ioctl,&rv, &fd))
- return rv;
- }
-
- /*
- ** All else fails so far.
- */
- errno = ENOTTY;
- return -1;
- }
-
- /*
- **
- ** The user callable entry point.
- **
- */
- int ioctl(int fd, int cmd, ...){
- va_list args;
- int argcx,argdx,argsi,argdi;
- int narg,xarg;
- va_start(args,cmd);
-
- if(__IS_UNIX_IOCTL(cmd)){
- int arg = va_arg(args, int);
- va_end(args);
- #ifdef TEST
- {
- int inflg = (cmd & IOC_IN) == IOC_IN;
- int outflg = (cmd & IOC_OUT) == IOC_OUT;
- int voidflg = (cmd & IOC_VOID) == IOC_VOID;
-
- int size = (cmd >> 16) & IOCPARM_MASK;
- char i_class = (cmd &0xff00) >> 8;
- printf("Calling UNIX ioctl %x:\n"
- "Class:\t\t'%c'\n"
- "Inflag:\t\t%d\tOutflag:\t%d\tvoidflg:\t%d\n"
- "Size:\t\t%d\n",
- cmd & 0xffff, i_class,
- inflg,outflg,voidflg,size);
- }
- #endif
- return _unix_ioctl(fd,cmd,arg);
- }
- /* Handle a DOS request */
- /* extract arguments */
- narg = (cmd >> 12) & 3;
- argdx=argsi=argdi=xarg=0;
- argcx = va_arg(args,int);
-
- if (narg > 0) argdx = va_arg(args,int);
- if (narg > 1) argdi = va_arg(args,int);
- if (narg > 2) argsi = va_arg(args,int);
- if (cmd & DOS_BRAINDEAD) xarg = va_arg(args,int);
- va_end(args);
-
- return _dos_ioctl(fd,cmd,argcx,argdx,argsi,argdi,xarg);
- }
-
-
-
-
- #ifdef TEST
-
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <unistd.h>
-
- int main (int argc, char **argv){
- int fd;
- int res;
- short *s;
- char mybuf[512];
-
- if(-1== (fd=open("NUL",O_RDWR))){
- return 2;
- }
- printf("fd: %d\n",fd);
-
- res = ioctl(fd,DOS_GETDEVDATA,0,0);
- printf("nul:\tDEV_DATA: %x\n",res ) ;
- res = ioctl(fileno(stdout),DOS_GETDEVDATA,0,0);
- printf("stdout:\tDEV_DATA: %x\n",res ) ;
- res = ioctl(fileno(stdin ),DOS_GETDEVDATA,0,0);
- printf("stdin:\tDEV_DATA: %x\n",res ) ;
- close(fd);
- fd = open("ioctl.c",O_RDONLY);
- res = ioctl(fd,DOS_GETDEVDATA,0,0);
- printf("ioctl.c:\tDEV_DATA: %x\n",res ) ;
- close(fd);
- fd=open("EMMQXXX0",O_RDONLY);
-
- mybuf[0] = '\0';
- mybuf[1] = '\0';
- res = ioctl(fd,DOS_RCVDATA,6,&mybuf);
- s = (short *) &mybuf;
- if (*s == 0x25) printf("EMM386 > 4.45\n");
- mybuf[0] = '\x02';
- mybuf[1] = '\0';
- res = ioctl(fd,DOS_RCVDATA,2,&mybuf);
- printf("res: %d\tEMM Version %d.%d\n",res,(int )mybuf[0],(int) mybuf[1]);
- close(fd);
-
- /*
- **
- ** The generic ioctls need an extra parameter, because CX no longer
- ** holds the size for the buffer.
- **
- */
- /* It would work like this:
-
- res=ioctl(fd,DOS_GENCHARREQ,category_code,&mybuf,driver_cmd,driver_cmd,
- sizeof(buffer table));
- R.Brown's interrupt list is not too specific about the role of SI and DI.
- It is important for European DOS 4.0 and OS/2 comp box. He just says
- they are the "parameter to pass to driver", whatever that might mean.
- I guess you can safely set the to 0.
-
- */
-
- printf("\n\nTIOCGETD = %x\n",TIOCGETD); ioctl(0,TIOCGETD,NULL);
- printf("TIOCSETD = %x\n",TIOCSETD); ioctl(0,TIOCSETD,NULL);
- printf("TIOCHPCL = %x\n",TIOCHPCL); ioctl(0,TIOCHPCL,NULL);
- return 0;
- }
- #endif
-
-