namespace Pciutil {

  extern "C include" {
    u8* coerce_ul_to_u8star(u32 l) {
      return l;
    }
  }export { * }

  //Use this to wrap pci resource initialization
  //This is a candidate for a cyc library function
  typedef struct pci_resource_response {
    <`i::I>
    u8 *{valueof(`i)}@nozeroterm iomem;
    tag_t<`i> io_len;
    u32 iostart;
    u32 ioend;
    u32 ioflags;
    u32 rc;
  } pci_response_t;

  static pci_response_t initialize_pci_resource(struct pci_dev<`a> *pdev, int mode, bool remap) {
    u32 st=0, end=0, flags=0, len=0, rc=0;
    pci_response_t res;
    u8 *@nozeroterm raw_ioaddr;
  
    st = pci_resource_start (pdev, mode);
    end = pci_resource_end (pdev, mode);
    flags = pci_resource_flags (pdev, mode);
    len = pci_resource_len (pdev, mode);
    /* ioremap MMIO region */
    if(remap) {
      raw_ioaddr = ioremap (st, len); //returns u8*
      if (!raw_ioaddr) {
	printk (KERN_ERR "%s: cannot remap MMIO, aborting\n", pdev->slot_name);
	rc = -EIO;
	goto err_out;
      }
    }
    else 
      raw_ioaddr = coerce_ul_to_u8star(st);
  
    res = pci_resource_response{raw_ioaddr, len, st, end, flags, 0};
    return res;
  
  err_out:
    res = pci_resource_response{0, 0, 0, 0, 0, rc};  
    return res;
  }

  //use this for iomemory
  //in most cases use the dependent type below to support a proof
  //that's required by Core::mkfat. Using fat ptrs saves making huge
  //structs like rtl_private below into existential types with the 
  //accompanying pattern matching hassles.
  //This is a candidate for a cyc library function
  struct bounded_iobuf {
    <`i::I>
    u8 *{valueof(`i)}@nozeroterm buf;
    tag_t<`i> len;
  };

  typedef struct bounded_iobuf bnd_buf_t;

  static bnd_buf_t pci_alloc(struct pci_dev<`a> *hwdev, size_t tag, dma_addr_t *dma_handle) {
    u8 *virt_addr = pci_alloc_consistent(hwdev, tag, dma_handle);
    bnd_buf_t res = bounded_iobuf{.buf = virt_addr, .len = tag};
    return res;
  }

  extern "C include" {
    static void* cyc_pci_alloc_consistent(struct pci_dev *pdev, unsigned int sz, int num, dma_addr_t *dma_hndl) {
      return pci_alloc_consistent(pdev, sz*num, dma_hndl);
    }
  }
  cyclone_override {
    static `a *cyc_pci_alloc_consistent(struct pci_dev<`x> *pdev, sizeof_t<`a> sz, int num, dma_addr_t *dma_hndl);
  }
  export { * }

  struct pci_array<`a> {
    <`i::I>
    `a *{valueof(`i)}@nozeroterm elts;
    tag_t<`i> len;
  };

  typedef struct pci_array<`a> pci_array_t<`a>;

  static pci_array_t<`a> pci_alloc_array(struct pci_dev<`x> *pdev, sizeof_t<`a> sz, int num, dma_addr_t *dma_hndl) {
    return pci_array{.elts=cyc_pci_alloc_consistent(pdev, sz, num, dma_hndl), .len=num};
  }

  static u8 ?@nozeroterm ioremap_fat(u8 *{valueof(`n)}@nozeroterm ioaddr, int nelts)
    @requires(nelts == numelts(ioaddr)) 
  {
    u8*@nozeroterm mio = ioremap(ioaddr, nelts);
    if(!mio)
      return 0;
    struct pci_array<u8> tmp = pci_array{.elts=mio, .len=nelts};
    let pci_array{<`j> .elts=tm, .len=tl} = tmp;
    return Core::mkfat(tm, sizeof(u8), tl);
  }

#define ASSERT_BOUND(tvar, fat, thin, bnd) \
  Pciutil::pci_array_t<u8> __assert_ ## fat = pci_array{.elts=(u8*@nozeroterm)port, .len=numelts(fat)}; \
  let Pciutil::pci_array { <`_ ##tvar> .elts= _##thin , .len=_##bnd } =  __assert_ ## fat
}
