Metropoli BBS
VIEWER: mmap.c MODE: TEXT (ASCII)
//   OS/2 Device Driver for memory mapped I/O
//
//                Steve Mastrianni
//                15 Great Oak Lane
//                Unionville, CT 06085
//                (203) 693-0404 voice
//                (203) 693-9042 data
//                CI$ 71501,1652
//                BIX smastrianni
//                Internet 6099225@mcimail.com
//
//   This driver is loaded in the config.sys file with the DEVICE=
//   statement. For ISA configuration, the first parameter to the "DEVICE="
//   is the board base memory address in hex.
//
//   This driver also returns a boolean to the calling application to
//   inform it of the bus type (Micro Channel or ISA).
//
//   All numbers are in hex. For MCA configuration, the board address 
//   is read from the board POS regs. The POS regs data is specific for
//   each adapter, so the address calculations here may not work with 
//   your specific adapter. Refer to the hardware tech reference for the
//   particular adapter to determine where and how the address appears
//   in the POS registers.
//
//
//   This driver allows the application I/O to run in Ring 2 with IOPL.
//   The CONFIG.SYS files *must* contain the IOPL=YES statement.
//
//   This driver supports 4 IOCtls, Category 0x90.
//
//   IOCtl 0x01 test for MCA or ISA bus
//   IOCtl 0x02 gets and returns a selector to fabricated board memory
//   IOCtl 0x03 gets the value of a selected POS register
//   IOCtl 0x04 gets the board address that the driver found
//
//   The driver is made by using the make file mmap.mak.

#include "drvlib.h" 
#include "mmap.h"

extern void near STRATEGY();        // name of strat rout. in DDSTART      

DEVICEHDR devhdr = {
        (void far *) 0xFFFFFFFF,    // link                                 
        (DAW_CHR | DAW_OPN | DAW_LEVEL1),// attribute                      
        (OFF) STRATEGY,             // &strategy                        
        (OFF) 0,                    // &IDCroutine                        
        "MMAP$   "
};

FPFUNCTION  DevHlp=0;               // storage area for DevHlp calls       
LHANDLE     lock_seg_han;           // handle for locking appl. segment    
PHYSADDR    appl_buffer=0;          // address of caller's buffer          
PREQPACKET  p=0L;                   // pointer to request packet           
ERRCODE     err=0;                  // error return                        
void        far *ptr;               // temp far pointer                    
USHORT      i,j;                    // general counters                    
PHYSADDR    board_address;          // base board address                  
USHORT      opencount;              // count of DosOpens                   
USHORT      savepid;                // save the caller's PID               
USHORT      cntr = 0;               // misc counter                        
USHORT      bus = 0;                // default ISA bus                     
REQBLK      ABIOS_r_blk;            // ABIOS request block                 
LIDBLK      ABIOS_l_blk;            // ABIOS LID block                     
USHORT      lid_blk_size;           // size of LID block                   
CARD        card[MAX_NUM_SLOTS+1];  // array for IDs and POS reg values    
CARD        *pcard;                 // pointer to card array               
USHORT      matches = 0;            // match flag for card ID              
POS_STRUCT  pos_struct;             // struct to get POS reg               
ADDR_STRUCT addr_struct;            // struct for passing addresses        
USHORT      chunk1,chunk2;          // temp variables for address calc     

char     arguments[64]={0};         // save command line args in dgroup    
char     NoMatchMsg[]  = " no match for selected Micro Channel card ID found.\r\n";
char     MainMsgMCA[]  = "\r\nOS/2 Micro Channel memory-mapped driver installed.\r\n";
char     MainMsgISA[]  = "\r\nOS/2 ISA bus memory-mapped driver installed.\r\n";

// prototypes 

int      hex2bin(char c);
USHORT   get_POS();
UCHAR    get_pos_data();
UCHAR    nget_pos_data();

// common entry point for calls to Strategy routines 

int main(PREQPACKET rp )
{
    void far *ptr;
    int far *pptr;
    PLINFOSEG liptr;                // pointer to local info seg           
    int i;
    ULONG addr;
    USHORT in_data;

    switch(rp->RPcommand)
    {
    case RPINIT:                    // 0x00                                

        // init called by kernel in protected mode ring 3 with IOPL 

        return Init(rp);

    case RPOPEN:                    // 0x0d                                

        // get current processes id 

        if (GetDOSVar(2,&ptr))
            return (RPDONE | RPERR | ERROR_BAD_COMMAND);

        // get process info 

        liptr = *((PLINFOSEG far *) ptr);

        // if this device never opened, can be opened by any process       

        if (opencount == 0)    // first time this device opened       
        {
            opencount=1;                 // set open counter                    
            savepid = liptr->pidCurrent; // save current process id   
        }
        else
            {
            if (savepid != liptr->pidCurrent) // another proc tried to open 
                return (RPDONE | RPERR | RPBUSY ); // so return error      
            ++opencount;            // bump counter, same pid              
        }
        return (RPDONE);

    case RPCLOSE:                   // 0x0e                                

        // get process info of caller 

        if (GetDOSVar(2,&ptr))
            return (RPDONE | RPERR | ERROR_BAD_COMMAND); // no info        

        // get process info from os/2 

        liptr= *((PLINFOSEG far *) ptr); // ptr to process info seg        

        // 
        // make sure that process attempting to close this device 
        // one that originally opened it and the device was open in
        // first place.
        //

        if (savepid != liptr->pidCurrent || opencount == 0)  
            return (RPDONE | RPERR | ERROR_BAD_COMMAND);

        // if an LDT selector was allocated, free it 

        PhysToUVirt(board_address,0x8000,2,&addr_struct.mapped_addr);
 
        --opencount;                // close counts down open counter      
        return (RPDONE);            // return 'done' status to caller      

    case RPREAD:                    // 0x04                                

        return(RPDONE);

    case RPWRITE:                   // 0x08                                

        return (RPDONE);

    case RPIOCTL:                   // 0x10                                

        if (rp->s.IOCtl.category != OUR_CAT) // only our category          
            return (RPDONE);

        switch (rp->s.IOCtl.function)
        {

		  // this IOCtl returns the bus type. If the type is Micro Channel
		  // the return is 0xff01. If ISA, the return is ff00

        case 0x01:                  // check if MCA or ISA
            return (RPDONE | RPERR | bus);

		  // this IOCtl maps an adapter memory to an LDT selector:offset,
		  // and sends it to the application for direct application reads
		  // and writes

        case 0x02:                  // send memory-mapped addr to app   

            // verify caller owns this buffer area 

            if(VerifyAccess(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            OFFSETOF(rp->s.IOCtl.buffer),   // offset                      
            8,                              // 8 bytes                     
            1) )                            // read write                  
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            // lock the segment down temp 

            if(LockSeg(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            0,                              // lock < 2 sec                
            0,                              // wait for seg lock           
            (PLHANDLE) &lock_seg_han))      // handle returned             
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // map the board address to an LDT entry

           if ( PhysToUVirt(board_address,0x8000,1,&addr_struct.mapped_addr)) 
               return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // move data to users buffer 

           if(MoveBytes(
           &addr_struct,                   // source
           rp->s.IOCtl.buffer,             // dest                      
           8))                             // 8 bytes                     
               return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // unlock segment 

           if(UnLockSeg(lock_seg_han))
               return(RPDONE | RPERR | ERROR_GEN_FAILURE);

           return (RPDONE);

		  // this IOCtl demonstrates how an application program can get the
		  // contents of a Micro Channel Adapter's POS registers

        case 0x03:                  // get pos reg data                    

            // verify caller owns this buffer area 

            if(VerifyAccess(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            OFFSETOF(rp->s.IOCtl.buffer),   // offset                      
            6,                              // 6 bytes                     
            1) )                            // read write                  
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            // lock the segment down temp 

            if(LockSeg(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            0,                              // lock < 2 sec                
            0,                              // wait for seg lock           
            (PLHANDLE) &lock_seg_han))      // handle returned             
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            // move slot data to driver buffer 

            if(MoveBytes(
            (FARPOINTER) appl_buffer,       // source                      
            &pos_struct,                    // for pos data                
            6))                             // 6 bytes                     
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            pos_struct.data = get_pos_data(pos_struct.slot,pos_struct.reg);

            // move POS reg data to users buffer 

            if(MoveBytes(
            &pos_struct,                    // for pos data                
            (FARPOINTER) appl_buffer,       // source                      
            6))                             // 6 bytes                     
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            // unlock segment 

            if(UnLockSeg(lock_seg_han))

                return(RPDONE | RPERR | ERROR_GEN_FAILURE);

            return (RPDONE);

		  // this IOCtl is essentially the same as 0x02, except the
		  // user virtual address is mapped to a linear address in the
		  // process address range and then sent to the application. This
		  // save the SelToFlat and FlatToSel each time the pointer is
		  // referenced.

        case 0x04:                  // 32-bit memory-mapped addr to app   

            // verify caller owns this buffer area 

            if(VerifyAccess(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            OFFSETOF(rp->s.IOCtl.buffer),   // offset                      
            8,                              // 8 bytes                     
            1) )                            // read write                  
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

            // lock the segment down temp 

            if(LockSeg(
            SELECTOROF(rp->s.IOCtl.buffer), // selector                    
            0,                              // lock < 2 sec                
            0,                              // wait for seg lock           
            (PLHANDLE) &lock_seg_han))      // handle returned             
                return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // map the board address to an LDT entry

           if ( PhysToUVirt(board_address,0x8000,1,&addr_struct.mapped_addr)) 
               return (RPDONE | RPERR | ERROR_GEN_FAILURE);

			  // now convert it to a linear address

			  if (VirtToLin((FARPOINTER)addr_struct.mapped_addr,
								(PLINADDR)&addr_struct.mapped_addr))
               return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // move data to users buffer 

           if(MoveBytes(
           &addr_struct,                   // source
           rp->s.IOCtl.buffer,             // dest                      
           8))                             // 8 bytes                     
               return (RPDONE | RPERR | ERROR_GEN_FAILURE);

           // unlock segment 

           if(UnLockSeg(lock_seg_han))
               return(RPDONE | RPERR | ERROR_GEN_FAILURE);

           return (RPDONE);

        } // switch (rp->s.IOCtl.function 

    case RPDEINSTALL:               // 0x14                            

        return(RPDONE | RPERR | ERROR_BAD_COMMAND);

        // all other commands are ignored 

    default:
        return(RPDONE);

    }
}

int  hex2bin(char c)
{
 if(c < 0x3a)
  return (c - 48);
 else
  return (( c & 0xdf) - 55);
}

USHORT get_POS(USHORT slot_num,USHORT far *card_ID,UCHAR far *pos_regs)
{
USHORT rc, i, lid;
    
    if (GetLIDEntry(0x10, 0, 1, &lid)) // get LID for POS   
        return (1);

    // Get the size of the LID request block 

    ABIOS_l_blk.f_parms.req_blk_len = sizeof(struct lid_block_def);
    ABIOS_l_blk.f_parms.LID = lid;
    ABIOS_l_blk.f_parms.unit = 0;;
    ABIOS_l_blk.f_parms.function = GET_LID_BLOCK_SIZE;
    ABIOS_l_blk.f_parms.ret_code = 0x5a5a;
    ABIOS_l_blk.f_parms.time_out = 0;

    if (ABIOSCall(lid,0,(void far *)&ABIOS_l_blk))
        return (1);

    lid_blk_size = ABIOS_l_blk.s_parms.blk_size; // Get the block size  

    // Fill POS regs and card ID with FF in case this does not work          

    *card_ID = 0xFFFF;
    for (i=0; i<NUM_POS_BYTES; i++) { pos_regs[i] = 0x00; }; 

    // Get the POS registers and card ID for the commanded slot 

    ABIOS_r_blk.f_parms.req_blk_len = lid_blk_size;
    ABIOS_r_blk.f_parms.LID = lid;
    ABIOS_r_blk.f_parms.unit = 0;;
    ABIOS_r_blk.f_parms.function = READ_POS_REGS_CARD;
    ABIOS_r_blk.f_parms.ret_code = 0x5a5a;
    ABIOS_r_blk.f_parms.time_out = 0;
    
    ABIOS_r_blk.s_parms.slot_num = (UCHAR)slot_num & 0x0F;
    ABIOS_r_blk.s_parms.pos_buf = (void far *)pos_regs;
    ABIOS_r_blk.s_parms.card_ID = 0xFFFF;
    
    if (ABIOSCall(lid,0,(void far *)&ABIOS_r_blk))
       rc = 1;
     else {                                       // Else                 
       *card_ID = ABIOS_r_blk.s_parms.card_ID;   //    Set the card ID value     
       rc = 0;
      }
    FreeLIDEntry(lid);
    return(rc);
    
}

UCHAR get_pos_data (int slot, int reg)
{
   UCHAR pos;
   CARD *cptr;

   cptr = &card[slot-1];            // set pointer to beg of card array    
   if (reg == 0)                    // card ID                             
      pos = LOUSHORT(cptr->card_ID);
   else
     if ( reg == 1)
      pos = HIUSHORT(cptr->card_ID);
   else
      pos = cptr->pos_regs[reg-2];  // POS data register                   
   return (pos);
}

// Device Initialization Routine 

int Init(PREQPACKET rp)
{
    USHORT lid;

    register char far *p;

    // store DevHlp entry point 

    DevHlp = rp->s.Init.DevHlp;  // save DevHlp entry point             

    if (!(GetLIDEntry(0x10, 0, 1, &lid))) { // get LID for POS   
       FreeLIDEntry(lid);

  // Micro Channel (tm) setup section 

  bus = 1;                      // MCA bus                             

      //    Get the POS data and card ID for each of 8 possible slots 

      for (i=0;i <= MAX_NUM_SLOTS; i++) 
         get_POS(i+1,(FARPOINTER)&card[i].card_ID,(FARPOINTER)card[i].pos_regs);

      matches = 0;
      for (i=0, pcard = card; i <= MAX_NUM_SLOTS; i++, pcard++) {
         if (pcard->card_ID == TARGET_ID) { 
            matches = 1;
            break;
            }
         }

      if (matches == 0) {           // at least one board found          
   DosPutMessage(1, 8, devhdr.DHname);
   DosPutMessage(1,strlen(NoMatchMsg),NoMatchMsg);
     rp->s.InitExit.finalCS = (OFF) 0;
   rp->s.InitExit.finalDS = (OFF) 0;
   return (RPDONE | RPERR | ERROR_BAD_COMMAND);
   }

      // calculate the board address from the POS regs 

    board_address = ((unsigned long) get_pos_data(i+1, 4) << 16) |
    ((unsigned long)(get_pos_data(i+1, 3) & 1) << 15);
  }

  else

  // ISA bus setup 

  {
  bus = 0;                      // ISA bus                             

  // get parameters, IRQ (not used yet), port addr and base mem addr 
  
  for (p = rp->s.Init.args; *p && *p != ' ';++p);// skip driver name 
  for (; *p == ' '; ++p);       // skip blanks following driver name   
  if (*p)
   {
         board_address=0;           // i/o port address                    
   for (; *p != '\0'; ++p)    // get board address                   
    board_address = (board_address << 4) + (hex2bin(*p));
   addr_struct.board_addr = board_address;
   }
  }

  if (bus)
         DosPutMessage(1,strlen(MainMsgMCA),MainMsgMCA);
      else
         DosPutMessage(1,strlen(MainMsgISA),MainMsgISA);

  // send back our cs and ds end values to os/2 
        
  if (SegLimit(HIUSHORT((void far *) Init), &rp->s.InitExit.finalCS) ||
     SegLimit(HIUSHORT((void far *) MainMsgISA), &rp->s.InitExit.finalDS))
       Abort();
  
  Beep(200,500);
  Beep(200,500);
  Beep(250,500);
  Beep(300,500);
  Beep(250,500);
  Beep(300,500);

  return (RPDONE);
  
}
[ RETURN TO DIRECTORY ]