/*
 * PnP, ISA Plug'n'Play configuration manager
 * Copyright 1996, David Howells (dwh@nexor.co.uk)
 * 
 * For more information, please consult 
 * 
 * Plug and Play ISA Specification
 * (c) 1993/4 Intel/Microsoft
 *
 */

#ifndef LINUX_PNP_H
#define LINUX_PNP_H

#include <linux/types.h>
#include <linux/ioctl.h>

extern int pnp_init(void);

/* define the type for a CSN */
typedef u_char csn_t;
typedef u_int eisa_t;

typedef struct _pnp_driver pnp_driver;
typedef struct _pnp_interface pnp_interface;
typedef struct _pnp_card pnp_card;
typedef struct _pnp_device pnp_device;
typedef struct _pnp_config pnp_config;

/* pnp interface def */
struct _pnp_interface {
    char pi_ident[5];	/* /proc interface identifier */

    /* read a configuration from a device */
    int (*pi_get_config)(pnp_device *, pnp_config *);

    /* configure device */
    int (*pi_set_config)(pnp_device *, const pnp_config *);

    /* (de)activate device */
    int (*pi_set_active)(pnp_device *, int);

    /* comment on a device for /proc/pnp */
    int (*pi_comment)(pnp_device *, char *, int);
};

/* pnp card def */
struct _pnp_card {
    pnp_card		*pc_next;
    pnp_interface	*pc_interface;	/* I/O interface */
    csn_t		pc_csn;		/* csn assigned to card */
    u_char		pc_ndev;	/* number of logical devices on card */
    eisa_t		pc_id;		/* vendor ID num */
    u_int		pc_unique;	/* unique dev num */
    u_char		*pc_resources;	/* resource list from card */
    u_short		pc_ressize;	/* size of resource list */
};

/* pnp card logical device def */
struct _pnp_device {
    pnp_device		*pd_next;
    pnp_card		*pd_card;	/* parent card */
    pnp_driver		*pd_driver;	/* driver handling this device */
    eisa_t		pd_id;		/* device ID num */
    u_short		pd_ressize;	/* size of resource list for dev */
    u_short		pd_resoffset;	/* offset in card resource list */
    u_char		pd_dev;		/* device number on card */
    u_char		pd_ifdata[7];	/* interface specific data */
    u_int		pd_drvdata;	/* driver specific data */
};

/* discovered pnp card and device lists */
extern pnp_card *pnp_card_list;
extern pnp_device *pnp_device_list;
extern csn_t pnp_isa_last_csn;

/*===========================================================================*/
/* current configuration list/new config request */
struct pnp_resource_list {
    u_char irq;		/* bits 0..1 set to indicate IRQ resources used */
    u_char dma;		/* bits 0..1 set to indicate DMA resources used */
    u_char io;		/* bits 0..7 set to indicate IO resources used */
    u_char mem;		/* bits 0..3 set to indicate MEM(32) resources used */
};

struct _pnp_config {
    u_char pc_active;	/* contents of active register (R/O here) */
    struct pnp_resource_list pc_s;

    u_long irqflags[2];	/* Flags for is_irq_available() for each IRQ */

    struct ___PNP_IRQ {
	u_char num;	/* IRQ number */
	u_char type;	/* IRQ type */
    } pc_irq[2];

    struct ___PNP_DMA {
	u_char chan;	/* DMA channel */
    } pc_dma[2];

    struct ___PNP_IO {
	u_short base;	/* IO port base */
    } pc_io[8];

    struct ___PNP_MEM {
	void *base;	/* MEM base */
	void *top;	/* MEM top */
	u_char type;	/* MEM type 0=8b 1=16b 3=32b */
    } pc_mem[4];

    u_char pc_memtype;	/* MEM addr type 0=24-bit 1=32-bit */
    u_char pc_force;	/* true if no-kernel-check requested */
};

/*===========================================================================*/
/* Hold one of the alternative (dependecy block) configurations available
 * Note that this holds ranges of potential configuration
 */
typedef struct _pnp_possibility {
    struct pnp_resource_list	pp_s;
        struct ___PNP_PP_IRQ {
	u_char nums[2];		/* IRQ numbers (bits set) */
	u_char type;		/* IRQ type bits */
	/*
	 * bit 3  -> true if supports low true, level sensitive
	 * bit 2  -> true if supports high true, level sensitive
	 * bit 1  -> true if supports low true, edge sensitive
	 * bit 0  -> true if supports high true, edge sensitive (mandatory)
	 */
    } pp_irq[2];

    struct ___PNP_PP_DMA {
	u_char chans[1];	/* DMA channels (bits set) */
	u_char type;		/* DMA type bits */
	/*
	 * bit 65 -> 00=compat-mode 01=type-A 10=type-B 11=type-F
	 * bit 4  -> true if supports count-by-word mode
	 * bit 3  -> true if supports count-by-byte mode
	 * bit 2  -> true if bus master
	 * bit 10 -> 00=8-bit 01=8-&16-bit 10=16-bit
	 */
    } pp_dma[2];

    struct ___PNP_PP_IO {
	u_short low;		/* IO port lowest base */
	u_short high;		/* IO port highest base */
	u_short align;		/* IO port alignment */
	u_short range;		/* IO port range length */
    } pp_io[8];

    u_char pp_memtype;		/* F=24-bit T=32-bit (mixing not permitted) */

    struct ___PNP_PP_MEM {
	void *low;		/* MEM/MEM32 lowest base */
	void *high;		/* MEM/MEM32 highest base */
	u_int align;		/* MEM/MEM32 alignment */
	u_int range;		/* MEM/MEM32 range length */
	u_char type;		/* MEM/MEM32 type flag */
	/*
	 * bit 6  -> ROM
	 * bit 5  -> Shadowable
	 * bit 43 -> 00=8bit only; 01=16bit only; 10=8/16bit; 11=32bit only
	 * bit 2  -> 0=range 1=high-addr
	 * bit 1  -> 0=no-cache 1=read-cache/write-thru
	 * bit 0  -> 0=R/O 1=R/W
	 */
    } pp_mem[4];
} pnp_possibility;

/*===========================================================================*/
/* driver control request flags */
typedef enum _PNP_DRV_CTRL {
    PNPDC_ACTIVATE,	/* activate driver */
    PNPDC_DEACTIVATE,	/* deactivate driver */
    PNPDC_RELEASE_RES,	/* release all kernel resources */
    PNPDC_LOCK,		/* request driver access lock (affect syscalls) */
    PNPDC_UNLOCK	/* unlock driver */
} PNP_DRV_CTRL;

/* pnp driver def */
struct _pnp_driver {
    pnp_driver		*pdr_next;
    const char		*pdr_name;		/* driver 'pretty' name */
    eisa_t		pdr_id;			/* EISA device ID num */
    u_long		pdr_irqflags[2];	/* IRQ type flags */

    /* attach/detach device to driver */
    int (*pdr_attach)(pnp_device *dev, int attach);
    
    /* control the driver (during device config) */
    int (*pdr_control)(pnp_device *dev, PNP_DRV_CTRL ctrl);

    /* RECONFIGURE
     * returns 0 if OK
     * returns -EAGAIN if configuration not quite right
     *  (also clears bits in conf->pc_s to indicate what's wrong)
     * returns other error to give up
     */
    int (*pdr_reconfigure)(pnp_device *dev, const pnp_possibility *poss,
			   pnp_config *conf);
};

/* calculations to convert chars into an EISA device ID (my storage format)
 * Eg: SB16 PnP Audio device: CTL0031
 *  eisa_t ID = PNPDRVID('C','T','L',0x003,0x1)
 */

#define PNPDRVID(V0,V1,V2,I,R) (					\
				((((V0|0x20)-'a'+1) & 0x001F) <<2)	| \
				((((V1|0x20)-'a'+1) & 0x0018) >>3)	| \
				((((V1|0x20)-'a'+1) & 0x0007) <<13)	| \
				((((V2|0x20)-'a'+1) & 0x001F) <<8)	| \
				((I&0x0FF0)<<12)			| \
				((I&0x000F)<<28)			| \
				((R&0x000F)<<24)			\
				)

#ifdef __KERNEL__
/* driver list */
extern pnp_driver *pnp_driver_list;
extern int pnp_register(pnp_driver *);
extern int pnp_unregister(pnp_driver *);

/*===========================================================================*/
/* get the def of a particular card */
extern inline pnp_card *pnp_find_card(csn_t csn) {
    pnp_card *card;
    for (card=pnp_card_list; card && card->pc_csn==csn; card=card->pc_next) {;}
    return card;
}

/* interface functions */
extern int pnp_read_resources(csn_t csn, u_char *resources, int len);
extern int pnp_get_res_dep_block(pnp_device *, int, pnp_possibility *);
extern void pnp_drv_deactivate(pnp_device *device);
extern void pnp_drv_activate(pnp_device *device);

/* extra kernel functions */
int is_dma_available(unsigned int dmanr);
int is_irq_available(int irq, unsigned long irqflags);
#endif __KERNEL__

/*===========================================================================*/
typedef struct _pnpioctl {
    u_char	pioc_req;	/* request */
    csn_t	pioc_csn;	/* csn of interest */
    dev_t	pioc_dev;	/* no. of device of interest */
    u_char	pioc_dep;	/* no. of dependecy block (query only) */
    union {
	pnp_config config;	/* read/set config */
	pnp_possibility poss;	/* query possibility */
    } pioc;
} pnpioctl;

/* ioctl for direct structure acquisition */
#define PNPIOC_REQUEST _IOWR(0x01,0,pnpioctl)

/* request IDs */
#define PNPREQ_QUERY 0
#define PNPREQ_READ 1
#define PNPREQ_SET 2
#define PNPREQ_ACTIVATE 3
#define PNPREQ_DEACTIVATE 4

/* ask for the current configuration of a device */
extern __inline__ int pnp_dev_current(pnp_device *device, pnp_config *conf) {
    return device->pd_card->pc_interface->pi_get_config(device,conf);
}

#endif LINUX_PNP_H
