#ifndef __KERNEL_DRIVER_H
#define __KERNEL_DRIVER_H

#ifdef __cplusplus
extern "C"
{
#endif

#include <sys/types.h>
#include <kernel/proc.h>
#include <kernel/handle.h>

/*!
 *  \ingroup	kernel
 *  \defgroup	dev Device Manager
 * @{
 */

#include <os/devreq.h>

typedef struct request_t request_t;

//! Contains information on a request issued to a device driver
struct request_t
{
	//! Specifies the request code (one of DEV_xxx)
	dword code;			// in
	//! Indicates the result of the request
	status_t result;		// out
	//! Triggered when the request has completed
	handle_t* event;	// out when calling devRequest;
						// in wrt driver

	//! Points to the next request in a driver's request list
	request_t* next;

	/* Internal members */
	size_t user_length;		// original user length
	request_t *kernel_request;	// in user mode, points to the copy made in kernel space
	size_t original_request_size;	// in kernel mode, the size of the original request
	int queued;				// is this request queued?

	union
	{
		//! General parameters for a buffered request
		struct
		{
			//! The buffer used
			addr_t buffer;
			//! Size of the buffer in bytes
			size_t length;
			//! The position where the buffered operation is to start
			/*! Ignored for character stream devices. */
			qword pos;
		} buffered;

		//! Parameters for a DEV_OPEN request
		struct
		{
			//! Specifies the parameters passed to devOpen()
			const wchar_t* params;
		} open;

		//! Parameters for a DEV_READ request
		struct
		{
			//! Specifies the buffer into which data are read
			void* buffer;		// out
			//! Specifies the number of bytes to read, and indicates the number
			//!	of bytes actually read
			size_t length;		// in/out
			//! Specifies the offset where reading is to start
			/*! Ignored for character stream devices. */
			qword pos;			// in
		} read;

		//! Parameters for a DEV_WRITE request
		struct
		{
			//! Specifies the buffer from which data are written
			const void* buffer;	// in
			//! Specifies the number of bytes to write, and indicates the number
			//!	of bytes actually written
			size_t length;		// in/out
			//! Specifies the offset where writing is to start
			/*! Ignored for character stream devices. */
			qword pos;			// in
		} write;

		//! Parameters for a DEV_IOCTL request
		struct
		{
			//! Specifies a buffer containing ioctl parameters and data
			void* buffer;		// in/out
			//! Specifies the size of the buffer
			size_t length;		// in/out
			//! Specifies the IOCTL function to perform
			dword code;		// in
		} ioctl;

		//! Parameters for a DEV_ISR request
		struct
		{
			//! Specifies the IRQ which was triggered
			byte irq;			// in
		} isr;

		//! Parameters for an FS_OPEN request
		struct
		{
			const wchar_t* name;		// in
			size_t name_length;			// in
			struct file_t* fd;			// out
		} fs_open, fs_create;

		//! Parameters for an FS_MOUNT request
		struct
		{
			const wchar_t* name;		// in
			size_t name_length;			// in
			struct device_t* dev;		// in
		} fs_mount;

		//! Parameters for FS_READ and FS_WRITE requests
		struct
		{
			addr_t buffer;				// out
			size_t length;				// in/out
			struct file_t* fd;			// in
		} fs_read, fs_write, fs_getlength;

		//! Parameters for an FS_CLOSE request
		struct
		{
			struct file_t* fd;			// in
		} fs_close;

		//! Parameters for a FS_IOCTL request
		struct
		{
			//! Specifies a buffer containing ioctl parameters and data
			void* buffer;		// in/out
			//! Specifies the size of the buffer
			size_t length;		// in/out
			//! Specifies the IOCTL function to perform
			dword code;		// in
			struct file_t* fd;
		} fs_ioctl;
	} params;
};

typedef struct device_t device_t;
typedef struct driver_t driver_t;

//! The prototype of the driver's request function
typedef bool (*DEVREQ)(device_t*, request_t*);

typedef struct device_config_t device_config_t;

//! The kernel structure used to contain information on each device
/*!
 * Since it is the device driver's responsibility to allocate memory for this
 *	structure, the driver author may place additional fields after this 
 *	structure. For example, it is possible to use an instance of this structure
 *	as the first member of a larger one.
 */
struct device_t
{
	//! The driver that registered this device
	driver_t* driver;
	//! The function to be called for each device request
	DEVREQ request;
	//! Pointer to the device's first queued request, if any
	request_t *req_first,
	//! Pointer to the device's last queued request, if any
		*req_last;
	device_config_t *config;
};

typedef struct device_resource_t device_resource_t;

enum { dresMemory, dresIo, dresIrq };
struct device_resource_t
{
	int cls;
	union
	{
		struct
		{
			addr_t base;
			size_t length;
		} memory;

		struct
		{
			addr_t base;
			size_t length;
		} io;

		byte irq;
	} u;
};

struct device_config_t
{
	device_t *parent;
	word vendor_id, device_id;
	dword subsystem;
	int num_resources;
	device_resource_t *resources;
};

//! The kernel structure used to maintain each device driver DLL
struct driver_t
{
	//! The module used to load this driver
	module_t* mod;
	device_t* (*add_device)(driver_t*, const wchar_t*, device_config_t*);
	device_t* (*mount_fs)(driver_t*, const wchar_t*, device_t*);
};

device_t*	devOpen(const wchar_t* name, const wchar_t* params);
bool		devClose(device_t* dev);
bool		devRemove(device_t* dev);
status_t	devRequest(device_t* dev, request_t* req);
status_t	devRequestSync(device_t* dev, request_t* req);
bool		devRegisterIrq(device_t* dev, byte irq, bool install);
driver_t*	devInstallNewDevice(const wchar_t* name, device_config_t* cfg);
bool		devRegister(const wchar_t* name, device_t* dev, device_config_t* cfg);
void		devStartRequest(device_t* dev, request_t* req);
void		devFinishRequest(device_t* dev, request_t* req);
status_t	devReadSync(device_t* dev, qword pos, void* buffer, size_t* length);
status_t	devWriteSync(device_t* dev, qword pos, const void* buffer, size_t* length);
status_t	devUserRequest(device_t* dev, request_t* req, size_t size);
status_t	devUserFinishRequest(request_t* req, bool delete_event);
dword		devFindResource(const device_config_t *cfg, int cls, int index);

#define		devIsBufferedRequest(code) (((int) (code)) < 0)
	
bool STDCALL	drvInit(driver_t*);
void		msleep(dword ms);

//@}

/*!
 *  \defgroup drivers Device Drivers
 */

#ifdef __cplusplus
}
#endif

#endif