From - Fri Oct 03 07:01:55 1997
Path: news.mitre.org!blanket.mitre.org!news.tufts.edu!cam-news-feed5.bbnplanet.com!cam-news-hub1.bbnplanet.com!su-news-hub1.bbnplanet.com!news.bbnplanet.com!news1.digital.com!pa.dec.com!nntpd.lkg.dec.com!not-for-mail
From: Fred Kleinsorge <kleinsorge@star.enet.dec_nospam.com>
Newsgroups: comp.os.vms
Subject: Re: Direct access to ISA board with VMS on Alpha
Date: Thu, 02 Oct 1997 12:39:54 -0400
Organization: OpenVMS Engineering
Lines: 273
Message-ID: <3433961A.69EF0535@star.enet.dec_nospam.com>
References: <01bcca27$124b6ec0$aa2a61ce@camelot> <342F802B.2D37@mail.dec.com.nospam> <01bccec8$80d428a0$372a61ce@camelot>
NNTP-Posting-Host: fgkaxp.zko.dec.com
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-Mailer: Mozilla 3.0b8Gold (X11; I; OpenVMS V7.1     DEC 3000 - M700)

I discourage attempting to PFN map and directly access IO space.
  Here are some code fragments that should allow IO space access to
  the ISA bus from a user application.  It should be platform
  independent.

  It's a bit of a hack, but it works (or the code I extracted it
  from does).




//
//  _READ_PORT
//
//  This macro is specific to the execute_isa_probe routine, and
//  is used to make the change mode to kernel call (just makes it
pretty).
//
#define _READ_PORT(_adp, _iohandle, _port_address, _size, _returns) {\
 arglist[0] = 5;\
 arglist[1] = (int) _adp;\
 arglist[2] = (int) _iohandle;\
 arglist[3] = (int) _port_address;\

 arglist[4] = (int) _size;\
 arglist[5] = (int) _returns;\
 status = sys$cmkrnl (read_io_port, arglist);\
}

//
//  _WRITE_PORT
//
//  This macro is specific to the execute_isa_probe routine, and
//  is used to make the change mode to kernel call (just makes it
pretty).
//
#define _WRITE_PORT(_adp, _iohandle, _port_address, _size, _data1) {\
 arglist[0] = 5;\
 arglist[1] = (int) _adp;\
 arglist[2] = (int) _iohandle;\
 arglist[3] = (int) _port_address;\
 arglist[4] = (int) _size;\
 arglist[5] = (int) _data1;\
 status = sys$cmkrnl (write_io_port, arglist);\
}


/*
 *  Routine:
 *
 *      read_io_port
 *
 *  Abstract:
 *
 *      This routine does a machine independent HW read.
 *      It is done as a change mode to kernel routine.
 *
 *  Inputs:
 *
 *      adp                     - adapter
 *      iohandle                - iohandle
 *      port                    - offset into address space
 *      length                  - 1, 2, or 4
 *      data_dw                 - address of a int32 to return the data
 *
 *  Outputs:
 *
 *      The data is read, shifted and masked.
 *
 *  Side Effects:
 *
 *      none
 *
 *  Completion Codes:
 *
 *      status returned by ioc$read_io
 *
 */
int
read_io_port(ADP *adp, uint64 *iohandle, int port,
                                         int length, int *data)
{
  int  status, shift, data_dw = 0xFFFFFFFF, mask;

  status = ioc$read_io(adp, iohandle, port, length, &data_dw);

  //
  //  Data is returned "byte laned", that is, it appears at the
  //  offset into a longword based on the low-order 2 bits of
  //  it's address.  So for words and bytes we need to shift it
  //  into it's expected place, and mask off any cruft in the
  //  longword.  This is known as data swizzling.
  //
  if (status & 1) {
    if (length == 1) mask = 0xFF;
      else if (length == 2) mask = 0xFFFF;
        else mask = 0xFFFFFFFF;
    shift = (port & 3) * 8;
    *data = (data_dw >> shift) & mask;
  }
  return (status);
}

/*
 *  Routine:
 *
 *      write_io_port
 *
 *  Abstract:
 *
 *      This routine does a machine independent HW write.
 *      It is done as a change mode to kernel routine.
 *
 *  Inputs:
 *
 *      adp                     - adapter
 *      iohandle                - iohandle
 *      port                    - offset into address space
 *      length                  - 1, 2, or 4
 *      data_dw                 - address of a int32 data
 *
 *  Outputs:
 *
 *      The shifted, masked, and written
 *
 *  Side Effects:
 *
 *      none
 *
 *  Completion Codes:
 *
 *      status returned by ioc$write_io
 *
 */
int
write_io_port(ADP *adp, uint64 *iohandle, int port,
                                          int length, int *data)
{
  int  status, shift, data_dw;

  //
  //  Da *
 */
int
write_io_port(ADP *adp, uint64 *iohandle, int port,
                                          int length, int *data)
{
  int  status, shift, data_dw;

  //
  //  Data must be "byte laned", that is, it must be shifted into
  //  a longword based on the low-order 2 bits of it's address.
  //  So for words and bytes we need to shift it into it's expected
  //  place.  This is known as data swizzling.
  //
  shift = (port & 3) * 8;
  data_dw = *data << shift;
  status = ioc$write_io(adp, iohandle, port, length, &data_dw);
  return (status);


/*
 *  Routine:
 *
 *      GetBaseISA_IOHANDLE
 *
 *  Abstract:
 *
 *      This routine finds the ISA or EISA adapter and
 *      returns the address of the iohandle structure
 *      so that a IOHANDLE can be fabricated.
 *      It checks that it is BUS_IO space with BYTE
 *      granularity, and maps 64kb.
 *
 *  Inputs:
 *
 *      none
 *
 *  Outputs:
 *
 *      IOHANDLE address
 *
 *  Side Effects:
 *
 *      none
 *
 *  Completion Codes:
 *
 *      status returned by ioc$write_io
 *
 */
int
GetBaseISA_IOHANDLE( ADP *return_adp)
{
  extern ADP *IOC$GL_ADPLIST;
  ADP *pADP;

  IOHANDLE *pIOHandle;
  pADP = IOC$GL_ADPLIST;

  //
  //  Here is a brief walk though the adapter list
  //  we are looking for the EISA or ISA adapter.
  //  If we find it, we'll verify that the IOHANDLE
  //  is valid, and return the IOHANDLE structure address.
  //
  while (pADP) {
    if (pADP->adp$l_adptype == AT$_ISA) {
      pIOHandle = pADP->adp$ps_iohandle_flink;
      if ((pIOHandle->iohandle$l_bus_region_size == 65536) &&
          (pIOHandle->iohandle$l_attributes ==
                             IOC$K_BUS_IO_BYTE_GRAN)) {
        *return_adp = pADP;
        return ((int) pADP->adp$ps_iohandle_flink);
      }
      else return (FALSE);
    } /* while (pADP) */
    else {
      if (pADP->adp$l_adptype == AT$_EISA) {
        pIOHandle = pADP->adp$ps_iohandle_flink;
        if ((pIOHandle->iohandle$l_bus_region_size == 65536) &&
            (pIOHandle->iohandle$l_attributes ==
                                      IOC$K_BUS_IO_BYTE_GRAN)) {
          *return_adp = pADP;
          return ((int) pADP->adp$ps_iohandle_flink);
        }
        else return (FALSE);
      }
    }
    if (pADP->adp$ps_child_adp != 0) {
      pADP = pADP->adp$ps_child_adp;
    }
    else {
      if (pADP->adp$ps_peer_adp != 0) {
        pADP = pADP->adp$ps_peer_adp;
      }
      else {
        while (pADP = pADP->adp$ps_parent_adp) {
          if (pADP->adp$ps_peer_adp != 0) {
            pADP = pADP->adp$ps_peer_adp;
            break;
          }
        }
        if (pADP == 0) return (FALSE);
      }
    }
  }
  return (FALSE);
}

//

foo() {

  ADP *pADP;

  int iohandle;
  int data = 1, arglist[10];


  iohandle = GetBaseISA_IOHANDLE( &pADP);

//  you can now use the _READ/_WRITE macro's to read/write IO space...
//  this writes a 1 to port address 310.

  _WRITE_PORT (pADP, &iohandle, 0x310, &data);
 
}

-- 
Frederick G. Kleinsorge       | Standard disclaimer:  All opinions
OpenVMS Engineering           | expressed are mine, and not those of my
Digital Equipment Corporation | employer, or any one else with half a
kleinsorge@star.enet.dec.com  | clue.  No refunds on sale items.
