Userdev Library for device drivers:
=====================================
This directory contains sources of the Userdev library consisting of functions 
to interact with the userdev driver and functions that form substitutes for 
kernel utilitites that are required by device drivers. 

The make utility compiles all the files and constructs the Userdev static library
and places it in the lib directory.

The contents of the directory:
=============================
- Makefile      		:make file for building the Userdev library
- README        		:this file 
- blk_req.h & blk_req.c		:block device request queue simulation library code
- siglist.h & siglist.c		:Data structure to keep track of list of messages received for various requests
- taskq.h & taskq.c		:Task queues simulation library
- timerq.h & timerq.c		:Kernel timers simulation library
- usrdelay.h & usrdelay.c	:Offers microsecond delay loop that uses busy waiting
- usrioreg.h & usrioreg.c	:Offers mutual exclusive access to I/O addresses across processes
- usrport.h & usrport.c		:Provides functions for setting permissions to I/O ports and for accessing the I/O ports
- usrdriv.h & usrdriv.c 	:Provides functions for interacting with the kernel userdev driver.


//////////////////////////////////////blk_req.h & blk_req.c///////////////////////////////////////////

Data structures used:
---------------------

-struct userdev_blk_request	/*each node of the block device request queue*/
	- device id
	- operation code
	- starting sector number
	- number of bytes to be transferred
	- cluster size: number of sectors
	- pointer to data buffer
	- a copy of the above, incase above is modified
	- size of the buffer
	- request id of userdev request
	- condition variable to signal completion of request
	- number of errors encountered while satisfying the request
	- pointer to next block device request in the request queue

Functions implemented:
----------------------

-struct userdev_blk_request* userdev_add_request(int devid, int command, long sector, int length, int clustersize, 
								void *data, int size, int reqid);
	function to add a request to the request queue given the device id, command, starting sector
	number, transfer length, cluster size, data to be transferred incase of read operation, the
	size of this data and the userdev request id. The function returns a pointer to the userdev_blk_request
	structure created and added to the request queue on success and NULL otherwise.

-void userdev_end_request(int res);
	function to remove a node from the top of the request queue and signal any threads waiting
	on the condition variable of the node. The function argument is dummy and is used to keep 
	the signature similar to the kernel function.

-void userdev_blk_requestq_cleanup();
	function to remove all requests in the request queue.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////siglist.h & siglist.c///////////////////////////////////////////

Data structures used:
--------------------

-struct userdev_sig_list	/*signal list node*/
	- request id for which the message has been received
	- pointer to next in the list

-struct userdev_sig_queue	/*signal list*/
	- pointer to first node in the list
	- mutex for accessing the list

Function implemented:
--------------------

-void userdev_add_sig_list(int reqid);
	function to add a node with the given request id to the signal list
-void userdev_del_sig_list(int reqid);
	function to remove all nodes in the signal list for the given request id
-int userdev_check_sig_list(int reqid);
	function to check if a signal message has arrived for the given request id. The function returns
	1 incase a node is found and 0 otherwise.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////taskq.h & taskq.c///////////////////////////////////////////

Data structures used:
--------------------

-struct userdev_task	/*data structure for each task*/
	- pointer to next task in the task queue
	- routine associated with the task that returns void and takes void* as argument
	- data to be passed to the above function
	- a flag to denote if the task is on the task queue or not, set to 1 if on taskqueue and 0 otherwise

-struct userdev_task_queue	/*task queue data structure*/
	- pointer to the first task on the task queue
	- mutex for accessing the task queue list
	- thread that executes the tasks in the task queue
	- flag set to stop the task queue thread
	- flag that denotes if the task queue can continue to run or not, set to 1 when task arrives and 0 when 
	task queue is empty
	- condition variable that would make the task thread wait for the above run flag to be set
	
Functions implemented:
---------------------

-void userdev_queue_task(struct task *tsk, struct task_queue *task_list);
	function to add the given task to the given task queue.
-static void* poll_task_queue(void *p);
	function to execute the tasks in the task queue passed as the argument. Its an internal function.
-void userdev_run_task_queue(struct task_queue *q, void (*fn)());
	function to create the task thread for the given task queue and assign the given function as the
	initialisation function.
-void userdev_task_queue_cleanup(struct task_queue *q);
	function to set the stop flag of the given task queue inorder to stop polling the task queue.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////timerq.h & timerq.c///////////////////////////////////////////

Data structures used:
--------------------

-struct userdev_timer_task	/*each task in the timer queue*/
	- pointer to the next timer task
	- pointer to the previous timer task
	- routine associated with the timer task that returns void and takes an unsigned long argument
	- unsigned long data to be passed to the above
	- expiry time of the timer task in jiffies at which the task has to be executed

-struct userdev_timer_queue	/*timer queue data structure*/
	- pointer to the first task in the timer queue
	- mutex for accessing the timer queue
	- flag that denotes if the timer queue is running, set to 1 when a timer task arrives and to 0 
	if timer queue is empty
	- condition variable to sleep if running is set to 0

Functions implemented:
---------------------

-void userdev_add_timer(struct timer_task *tsk);
	function to add the given timer task to the timer queue while keeping the queue sorted by expiry times
	of the tasks.

-int userdev_del_timer(struct timer_task *tsk);
	function to remove the given timer task from the timer queue. The function returns 1 if the task was
	found in the timer queue and 0 otherwise.

-int userdev_mod_timer(struct timer_task *tsk, unsigned long expires);
	function to modify the expiry time of the given timer task. The function returns 1 if the task is present
	in the timer queue and 0 otherwise.

-int userdev_timer_pending(struct timer_task *tsk);
	function to check if the given timer task is still pending to be executed ie it is still present in the
	timer queue. The function returns 1 if the timer task has been found and 0 otherwise.

-unsigned long userdev_get_jiffies();
	function that returns the current time in 100th of second since epoch. This function is used for specifying
	the expiry time of the timer tasks.

-static void* run_timer_queue (void* dummy);
	function that runs through the timer queue and executes all tasks that have expired. This is an internal
	function that is executed on the timer queue thread.

-void userdev_timer_queue_cleanup();
	function to set the timerq_stop flag inorder to stop the timer queue thread.

-void userdev_init_timer_queue(void (*fn)());
	function to initialize the timer queue, create a timer queue thread, install a signal handler for the alarm
	signal used to keep track of time when executing the tasks. The function argument is a pointer to the 
	initalization function to be executed when the timer thread begins.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////usrdelay.h & usrdelay.c///////////////////////////////////////////

Data structures used:
--------------------

- loops_per_usec	/*number of iterations of a predefined loop to produce a delay of 1 microsecond*/

Functions Implemented:
----------------------

-static void delay_loop(unsigned long l);
	function that forms the delay loop for busy waiting. The function argument is the number of iterations
	to make of the standard loop with in the function. This is an internal function.

-static struct timeval *diff(struct timeval *t1, struct timeval *t2);
	function to calculate the difference between two given time values and place the value in another
	timeval structure. The return value is the pointer to a static timeval structure that holds the
	difference. This function is therefore not thread safe. Anyway its an internal function called by
	calibrate dalay which has to be called at driver initialization time.

-void userdev_calibrate_delay();
	function to calibrate number of loops required for 1 microsecond delay. This function needs to be called
	before the delay function can be used as it calibrates the delay loop to determine number of loops for a
	delay of 1 microsecond. The values loops_per_usec cannot determined ahead as it depends on processor 
	speed of operation, load on the system etc 

-void userdev_udelay(unsigned long usec);
	function to be called to obtain a delay of given number of microseconds.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////usrioreg.h & usrioreg.c///////////////////////////////////////////

Functions Implemented:
---------------------

- static char *freadline(char *s, int size, int fd);
	function to read characters from the given file until the buffer becomes full or end-of-file is reached. The 
	function arguments are the pointer to the buffer, buffer size and file descriptor. The function returns a pointer
	to the buffer incase some data has been read or NULL otherwise.

- int userdev_check_ioregion(int base, int num);
	function to check if the I/O address space, specified by the given starting address and number of addresses
	is free. The function returns 0 if the region is free and negative number otherwise.
	
- int userdev_request_ioregion(int base, int num, const char *name);
	function to reserve the I/O address space, specified by the given starting address and number of addresses
	under the given device name. The function returns 0 on success and a negative number otherwise.
	
- int userdev_release_ioregion(int base, int num);
	function to release the I/O address space, specified by the given starting address and number of addresses.
	The function returns 0 on success and a negative number otherwise.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////usrport.h & usrport.c///////////////////////////////////////////

Functions Implemented:
---------------------

- int userdev_set_ioperm(int port);
	function to set the I/O permission for the current process on the given port number. The function 
	returns 0 on success and a negative error number otherwise.

- int userdev_reset_ioperm(int port);
	function to reset the I/O permission for the current process on the given port number. The function
	returns 0 on success and a negative error number otherwise.

- unsigned char userdev_inportb(int port);
	function to read a byte from the specified port. The value read is returned by the function.
	
- void userdev_outportb(int port, unsigned char byte);
	function to write a byte to the specified port number.

- unsigned short userdev_inportw(int port);
	function to read a word from the specified port. The value read is returned by the function.

- void userdev_outportw(int port, unsigned short byte);
	function to write a word to the specified port number.

- unsigned char userdev_inportd(int port);
	function to read a double word from the specified port. The value read is returned by the function.

- void userdev_outportd(int port, unsigned int byte);
	function to write a double word to the specified port number.

- int userdev_reset_all_perms();
	function to reset all the I/O permissions acquired by the current process.

//////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////usrdriv.h & usrdriv.c///////////////////////////////////////////

User level device driver Interface:
----------------------------------
- userdev file operations structure:
	This data structure contains pointers to functions to be implemented by a
	driver process. A pointer can be NULL incase no implementation exists.

	The devtype field denotes the type of the device as to whether it is a character 
	device or a block device. In each of the functions id denotes the userdev id of the
	driver process and reqid is the request id of the request packet received from kernel
	which needs to be passed to the send_response function which is used to send the
	response packet back to the kernel.

	The message function does not require us to send a response as it is only an intimation
	from the kernel. The request function is for block devices only.
	
	struct userdev_operations
	{
       		int devtype;	/*device type*/

		/*arguments->userdev id, size of data to be read, offset, file structure flags, request id*/
        	void (*read)(int id, unsigned int size, long long off, int flags, int reqid);

		/*arguments->userdev id, data to be written, size of data, offset, file structure flags, request id*/
        	void (*write)(int id, char *data, int len, long long off, int flags, int reqid);

		/*arguments->userdev id, request id*/
        	void (*poll)(int id, int reqid);

		/*arguments->userdev id, ioctl command, data, size of data, request id*/
        	void (*ioctl)(int id, int command, void* data, int size, int reqid);

		/*arguments->userdev id, file open flags, file open mode, request id*/
        	void (*open)(int id, unsigned int flags, mode_t mode, int reqid);

		/*arguments->userdev id, request id*/
        	void (*flush)(int id, int reqid);

		/*arguments->userdev id, request id*/
        	void (*close)(int id, int reqid);
		
		/*arguments->userdev id, data sync flag, request id*/
        	void (*fsync)(int id, int datasync, int reqid);

		/*arguments->userdev id, request id*/
        	void (*fasync)(int id, int reqid);

		/*arguments->userdev id, request id*/
        	void (*check_media_change)(int id, int reqid);
		
		/*arguments->userdev id, request id*/
        	void (*revalidate)(int id, int reqid);

		/*arguments->userdev id, optional arguments, request id*/
        	void (*mediactl)(int id, int op, int optarg, int reqid);

		/*arguments->userdev id, type of message, request id, optional data, data size*/
        	void (*message)(int id, int type, int reqid, void *data, int size);

		/*arguments->userdev id, command, sector number, transfer length, optional data, data size, request id*/
        	void (*request)(int id, int command, long sector, int length, void* data, int datasize, int reqid);
	};


Data structures:
----------------
- struct userdev_ioctl_data 	/*structure for representing ioctl information passed while attaching
				the process to userdev*/
        - ioctl command
        - max data length supported       

- struct userdev_blk_data	/*structure for representing block device information passed while 
				attaching the driver process*/
        - device size, in kilobytes
        - block size, in bytes
        - sector size, in bytes
        - maximum read ahead for buffer cache, in memory pages
        - maximum number of sectors in any request

- struct __userdev_info		/*internal to usrdriv.c*/
	- userdev id
	- minor number attached to
	- read pipe descriptors (pair)
	- write pipe descriptors (pair)
	- pointer to userdev operations passed during attach
	- signal number for interrupts

- __ud_info[MAX_DRIVERS]
	An array of pointers to __userdev_info structures which keep information for each driver
	process that attaches to userdev driver. This info needs to be stored in the library as this 
	information is provided during attach call but is used by subsequent calls for process detach,
	free irq, etc and most importantly the start function that despatches requests from the kernel
	to the driver process

- __ud_count: to keep count of number of processes attached
- userdev_chr & userdev_blk: names of userdev character and block device files (minor 0)

Functions implemented:
----------------------
- int userdev_attach(char *fname, struct userdev_ioctl_data *ioelm, int num_ioelms, struct userdev_operations *ops,
		void *data);
	This function is called to attach a user level driver process to the Userdev kernel driver. It takes
	the device filename to attach to, an array of ioctl data elements specifying the list of allowed ioctl
	numbers and maximum length of data handled by each, the number of ioctl data elements, a pointer to the
	driver's Userdev operations structure and an optional data parameter used for block devices. The Userdev
	operations structure is stored in the library's internal data structure, maintained for each attached 
	driver process, for use by other routines of the library. The function returns the Userdev id of the 
	driver process in case the attach operation had been successful and an error number in case an error was
	encountered.

- int userdev_detach(int id);
	This function is called to detach the driver process from Userdev kernel driver, identifying it using the
	Userdev id given as the function argument. The result of the detach operation is returned.

- int userdev_inform_poll_in(int id);
	This function is called to ask the Userdev kernel driver to inform the availability of data to anybody
	polling the device for input data. The Userdev id, passed as the function argument is used to identify 
	the attached driver process. The value returned by the function is the result of the operation.

- int userdev_inform_poll_out(int id);
	This function is called to ask the Userdev kernel driver to inform the availability of the device for 
	output to anybody polling the device for output. The Userdev id passed as the function argument is used
	to identify the attached driver process. The result of the operation is the return value of the function.

- int userdev_inform_fasync_io(int id);
	This function is called to ask the Userdev kernel driver to inform the availability of data to anyone 
	using asynchronous I/O and waiting for some I/O activity to take place. The Userdev id is passed as the
	function for identifying the attached driver process and the result of the operation is returned back to
	the caller of the function.

- int userdev_request_irq(int irq, int sig, void (*hdlr)(int, siginfo_t *, void *), int id);
	This function is called to request the Userdev kernel driver to inform about the occurrence of interrupts
	on a given IRQ line using a signal. The arguments to the function are the IRQ line number, the signal number
	corresponding to it, pointer to the signal handler function  and lastly the Userdev id of the attached driver
	process. The result of the operation is returned back to the caller.

- int userdev_free_irq(int id);
	This function is used to ask the Userdev kernel driver to stop signalling the occurrence of an interrupt.
	The result of the operation is returned back to the caller. The function also resets the signal disposition
	of the signal corresponding to an interrupt on the IRQ line to the default value.

- int userdev_request_dma(int dma, int id);
	This function is used to request for a DMA channel from the kernel. The arguments of the function are the
	DMA channel number and the Userdev id of the driver process. The result of the operation is returned to the
	caller.

- int userdev_free_dma(int id);
	This function is used to free the DMA channel acquired by the driver process to the kernel resource pool.
	The argument passed to the function is the Userdev id of the driver process. The result of the operation 
	is returned to the caller.

- int userdev_start_dma(int mode, int count, char *buf, int id);
	This function is used to start a DMA operation on the DMA channel acquired by the driver process. The 
	arguments to the function are the mode of the DMA operation, the data buffer, the data transfer length 
	and of course the Userdev id to identify the driver process. The return value of the function is the 
	result of the operation.

- int userdev_check_dma(int id);
	This function is used to obtain the status of an ongoing DMA operation. The Userdev id is passed as the 
	function argument to identify the driver process and the result of the operation, that is the number of 
	bytes remaining in the transfer is returned to the caller.

- int userdev_start();
	This function is the work horse function of the library and is called once all initializations have been 
	completed and the driver process is ready to serve requests from the Userdev kernel driver. This function
	does not return to the caller unless an error is encountered. It continuously obtains requests from the 
	Userdev kernel driver and calls the corresponding function of the Userdev operations structure specified 
	during the call to the userdevattach function.

- void userdev_send_response(int id, int reqid, int opcode, int retval, int size, void *data);
	This function is used to send the response to a request, obtained from the Userdev kernel driver, by the
	driver function that was called to serve the request. The arguments of the function are the Userdev id of
	the driver process, the request id of the request, the operation code, the result of the operation, the 
	size of data to be sent in the response and the data itself. The function has no return value.

- int userdev_copy_dma(char *buf, int id);
	This function is used to copy the contents of the kernel DMA buffer into the buffer passed as the function
	argument. The Userdev id of the driver process forms the other argument of the function. The function returns
	the result of the copy operation obtained from the kernel.

- int userdev_enable_dma(int id);
	This function enables a DMA operation incase one has been suspended. The Userdev id is passed as the function
	argument to identify the driver process sending the request. The return value of the function is the result
	of the operation returned by the kernel.

- int userdev_disable_dma(int id);
	This function disables a DMA operation incase one is in progress. The Userdev id is passed as the function
	argument to identify the driver process sending the request. The return value of the function is the result
	of the operation returned by the kernel.

