/*
 *
 */
 
#include "defsfile"

extern scf_drvr_stat drvrstatic;
extern error_code irqsort();
extern Scf_lu_stat lu_list;

Scf_lu_stat lu_list = NULL;        /* list of DIO logical units */

/* inithdwe - initialize the DIO chip. */
error_code init(register Dev_list device_entry)	/* device list entry pointer */
{
	register Scf_lu_stat next_lu;	    	/* device list pointer */
	register Scf_lu_stat unit_stat;		/* unit static storage pointer */
	register error_code  error;			/* error condition code */
	register int x;

	u_int32		addr;
	u_int8		bus, dev;
	u_int8		index;
	u_int16		bus_info;
	
	/* get the unit static storage pointer */
	unit_stat = device_entry->v_lu_stat;

	/*
	 * Each device contains the plx start address in PCI base address[1].
	 * Each device contains the start address in PCI base address[2].
	 *
	 * This must be configured in the v_port address:
	 *
	 * Each descriptor should contain the index of
	 * the PCI card in the lowest 8 bits [0-7]
	 *
	 * The PCI_VENDOR_ID and PCI_DEVICE_ID is configured in the
	 * descriptor under v_pci_ven_id und v_pci_dev_id.
	 *
	 */
	addr = (u_int32) unit_stat->v_port;	
	index = (addr & 0xFF);
	bus = 0;
	dev = 0;

	if (unit_stat->v_pci_ven_id!=ADLINK_PCI_VENDOR_ID || unit_stat->v_pci_dev_id==0 || 
         pci_find_device(unit_stat->v_pci_ven_id, unit_stat->v_pci_dev_id, index, &bus, &dev )!=SUCCESS)
	{
		return E_HARDWARE;
	}

	dev = dev & 0x1F;

	/* check for first card being attached */
	if (++drvrstatic.v_attached == 1)  
    {
		/* init lu list to the first lu attached */
		lu_list = unit_stat;
	} 
    else 
    {
		/* find last already attached logical unit in the list */
		for (next_lu = lu_list; (next_lu->v_next_lu != NULL) &&
            (((u_int32) next_lu->v_port & 0xFF) != index); )
			next_lu = next_lu->v_next_lu;

		/* check for logical unit already attached */
		if (((u_int32) next_lu->v_port & 0xFF) == index)
            return SUCCESS;
	
		/* link new logical unit onto list */
		next_lu->v_next_lu = unit_stat;
	}


	/* Make sure I/O bits are set */
	bus_info = pci_read_configuration_word(
		bus, 
		dev, 
		0,
		offsetof(pci_config_reg,command_reg)) 
		| B_PCIC_IO_SPACE;	

	pci_write_configuration_word(
		bus, dev, 0,
		offsetof(pci_config_reg,command_reg), bus_info);


	/* Get PCI irq line if needed */
	if (unit_stat->v_vector == 0)
		unit_stat->v_vector = pci_get_irq_line(bus, dev, 0) + IRQ_BASE_OFFSET;

	/* Get port address */
	unit_stat->v_ports[0] = (u_char *)( (pci_read_configuration_dword(\
		bus, 
		dev, 
		0, 
		offsetof_base_addrs(2)) & ~1 ) | PCI_IO_SPACE_BASE);

    /* Wipe out the other unused ports */
	for (x=1; x < NUMPORTS; x++)
		unit_stat->v_ports[x] = 0;
            
	unit_stat->v_plx_base = (u_char *)( (pci_read_configuration_dword(bus, dev, 0, 
				offsetof(pci_config_reg,base_addrs[1])) & ~1 ) | PCI_IO_SPACE_BASE);

	/*
	 * If the resulting (or passed) vector is 0
	 * return hardware error.
	*/

	if (unit_stat->v_vector == 0)
		return E_HARDWARE;

   	unit_stat->lms_m1=0;  /* clear magic-memory value about T1*/
    unit_stat->lms_m2=0;  /* clear magic-memory value about T2*/

    /* start and create events */
    unit_stat->lms_evh1 = 0;
    unit_stat->lms_evh2 = 0;
    if ((error=_os_ev_link(EV1_NAME, &unit_stat->lms_evh1)) != SUCCESS)
    {
  	   if ((error=_os_ev_creat(0, 1, 0xffff, &unit_stat->lms_evh1, EV1_NAME, 0, MEM_ANY))!=SUCCESS)
            return error;
    }

    if ((error=_os_ev_link(EV2_NAME, &unit_stat->lms_evh2)) != SUCCESS)
    {
        if ((error=_os_ev_creat(0, 1, 0xffff, &unit_stat->lms_evh2, EV2_NAME, 0, MEM_ANY))!=SUCCESS)
            return error;
    }
        
	/* set connector 1 to the following port configuration: port A = out, port B = in, port C = in */
	OUT(&unit_stat->PORTADDR[DIO_CONNCTRL], 0x0B);

	/* install interrupt service routine */
	if ((error=_os_irq(unit_stat->v_vector, unit_stat->v_priority, irqsort,
									   (void *) device_entry->v_dr_globs)) != SUCCESS)
		return error;
	
	/* enable ~P1C0 & P1C3 interrupts */
	OUT(&unit_stat->PORTADDR[DIO_ISC], 0x02);
    /* enable the IRQs on the card (in the PLX) */
    OUT(unit_stat->v_plx_base+PLX_INTCR, 0x53);	

    if ((error = _pic_enable(unit_stat->v_vector)) != SUCCESS)
		return error;

	/* send a null character to port A */
	OUT(&unit_stat->PORTADDR[DIO_PORTA], 0);

    return SUCCESS;
}
