Kernel module for User Level Device Driver (userdev)
===================================================
This directory contains code for the userdev kernel driver module. The code is organised into
various files, details of which are given below, which can be compiled and linked using the
make utility into a single loadable kernel module. 

The following commands can be given for the make utility:
make 		: to compile and link the module driver
make clean	: to remove all object files along with the userdev module
make install	: to install the kernel driver using insmod utility with userdev_major=248
make uninstall	: to uninstall the kernel driver using rmmod utility

Contents of the current directory
--------------------------------
- Makefile	:make file to build userdev kernel module
- README        :this file
- common.h	:file containing data structures common to kernel module and user level library
- errors.h	:file containing error codes for kernel module
- ioctls.h	:file containing ioctl numbers for kernel module
- helpers.h	:file containing declarations of functions in helpers.c
- helpers.c	:file containing helper functions for userdev 
- userdev.h	:file containing declarations of data structures and functions of userdev module
- userdev.c	:file containing the code for userdev module
- userdev	:userdev kernel module


////////////////////////////////////////////common.h/////////////////////////////////////////////

Data structures defined in this file are:
-----------------------------------------

- struct ioelm		/*to specify ioctls accepted by driver process*/
	- ioctl number
	- max allowed length

- struct attach_record	/*to specify driver process attributes during attach*/
	- read pipe descriptor
	- write pipe descriptor
	- device name
	- length of name
	- mask denoting list of functions implemented
	- number of ioctls supported
	- array of ioelms
	- optional data

- struct blkdev_data	/*to specify optional data values passed for block devices during attach*/
	- device size in kilo bytes
	- block size in bytes
	- sector size in bytes 
	- max read ahead in pages
	- maximum no: of sectors in a request

- struct detach_record	/*to specify attributes for driver process detach*/
	- userdev id

- struct irq_request_record	/*to specify attributes for requesting irq*/
	- userdev id
	- signal number of signal that denotes occurance of an interrupt
	- irq line number

- struct irq_free_record	/*to specify attributes for freeing irq*/
	- userdev id

- struct dma_request_record	/*to specify attributes for requesting dma channel*/
	- userdev id
	- dma channel

- struct dma_free_record	/*to specify attributes for freeing dma channel*/
	- userdev id

- struct dma_start_record	/*to specify attributes for starting a dma operation*/
	- userdev id
	- mode of dma operation
	- number of bytes
	- address to/from which transfer has to be made

- struct dma_check_record	/*to specify attributes for obtaining residue of the dma operation*/
	- userdev id

- struct dma_copy_record	/*to specify attributes for copying dma data from kernel to user space*/
	- userdev id
	- address to which data is to be copied

- struct header		/*header for packet that moves between driver process & the kernel module*/
	- operation code
	- request id
	- data packet size

- struct read_send	/*read request packet*/
	- amount to read
	- offset to read from
	- operation flags
- struct read_recv	/*read response packet*/
	- new offset
	- data read along with amount read

- struct write_send	/*write request packet*/
	- offset at which to write
	- operation flags
	- data to be written along with size
- struct write_recv	/*write response packet*/
	- new offset
	- number of bytes written

- struct poll_send	/*poll request packet*/
- struct poll_recv	/*poll response packet*/
	- poll result

- struct ioctl_send	/*ioctl request packet*/
	- ioctl command code
	- data sent along with size
- struct ioctl_recv	/*ioctl response packet*/
	- ioctl operation result
	- data received along with length

- struct open_send	/*open request packet*/
	- file open flags
	- file open mode
- struct open-recv	/*open response packet*/
	- result of open operation

- struct flush_send	/*flush request packet*/
- struct flush_recv	/*flush response packet*/
	- result of the operation

- struct close_send	/*close request packet*/
- struct close_recv	/*close response packet*/
	- result of the operation

- struct fsync_send	/*fsync request packet*/
	- flag to signify whether meta data needs to be synced too
- struct fsync_recv	/*fsync response packet*/
	- result of the operation

- struct fasync_send	/*fasync request packet*/
- struct fasync_recv	/*fasync response packet*/
	- result of the operation

- struct check_media_change_send	/*check_media_change request packet*/
- struct check_media_change_recv	/*check media change response packet*/
	- flag denoting result of operation
	
- struct revalidate_send	/*revalidate request packet*/
- struct revalidate_recv	/*revalidate response packet*/
	- result of media revalidation

- struct mediactl_send	/*mediactl request packet*/
	- arguments of mediactl
- struct mediavtl_recv	/*mediactl response packet*/
	- result of the operation

- struct message_send	/*message request packet*/
	- type of message
	- request id for which the message is being sent
	- size of data
	- data from message source

- struct request_send	/*block device request, request packet*/
	- sector number
	- operation code
	- length of data being handled
	- number of sectors in current cluster(number of sectors remaining to be 
	  serviced in current buffer_head list of the current request
	- data to be read/written along with size
- struct request_recv	/*block device request, response packet*/
	- result of the operation
	- data along with size incase of read operation


Functions implemented in this file:
----------------------------------

- int read_send_to_char(struct header *head, struct read_send* rs, char**pstr);
	To construct a string of bytes from header and read_send structures returning the length of the string

- struct read_send* read_send_from_char(char* str);
	To obtain read_send structure from the given string

- int read_recv_to_char(struct header *head, struct read_recv* rr, char**pstr);
	To construct a string of bytes from header and read_recv structures returning the length of the string

- struct read_recv* read_recv_from_char(char* str);
	To obtain read_recv structure from the given string


- int write_send_to_char(struct header *head, struct write_send* rs, char**pstr);
	To construct a string of bytes from header and write_send structures returning the length of the string

- struct write_send* write_send_from_char(char* str);
	To obtain write_send structure from the given string

- int write_recv_to_char(struct header *head, struct write_recv* rr, char**pstr);
	To construct a string of bytes from header and write_recv structures returning the length of the string

- struct write_recv* write_recv_from_char(char* str);
	To obtain write_recv structure from the given string


- int poll_send_to_char(struct header *head, struct poll_send* rs, char**pstr);
	To construct a string of bytes from header and poll_send structures returning the length of the string

- struct poll_send* poll_send_from_char(char* str);
	To obtain poll_send structure from the given string

- int poll_recv_to_char(struct header *head, struct poll_recv* rr, char**pstr);
	To construct a string of bytes from header and poll_recv structures returning the length of the string

- struct poll_recv* poll_recv_from_char(char* str);
	To obtain read_recv structure from the given string


- int ioctl_send_to_char(struct header *head, struct ioctl_send* rs, char**pstr);
	To construct a string of bytes from header and ioctl_send structures returning the length of the string

- struct ioctl_send* ioctl_send_from_char(char* str);
	To obtain ioctl_send structure from the given string

- int ioctl_recv_to_char(struct header *head, struct ioctl_recv* rr, char**pstr);
	To construct a string of bytes from header and ioctl_recv structures returning the length of the string

- struct ioctl_recv* ioctl_recv_from_char(char* str);
	To obtain ioctl_recv structure from the given string


- int open_send_to_char(struct header *head, struct open_send* rs, char**pstr);
	To construct a string of bytes from header and open_send structures returning the length of the string

- struct open_send* open_send_from_char(char* str);
	To obtain read_send structure from the given string

- int open_recv_to_char(struct header *head, struct open_recv* rr, char**pstr);
	To construct a string of bytes from header and open_recv structures returning the length of the string

- struct open_recv* open_recv_from_char(char* str);
	To obtain open_recv structure from the given string


- int flush_send_to_char(struct header *head, struct flush_send* rs, char**pstr);
	To construct a string of bytes from header and flush_send structures returning the length of the string

- struct flush_send* flush_send_from_char(char* str);
	To obtain flush_send structure from the given string

- int flush_recv_to_char(struct header *head, struct flush_recv* rr, char**pstr);
	To construct a string of bytes from header and flush_recv structures returning the length of the string

- struct flush_recv* flush_recv_from_char(char* str);
	To obtain flush_recv structure from the given string


- int close_send_to_char(struct header *head, struct close_send* rs, char**pstr);
	To construct a string of bytes from header and close_send structures returning the length of the string

- struct close_send* close_send_from_char(char* str);
	To obtain close_send structure from the given string

- int close_recv_to_char(struct header *head, struct close_recv* rr, char**pstr);
	To construct a string of bytes from header and close_recv structures returning the length of the string

- struct close_recv* close_recv_from_char(char* str);
	To obain close_recv structure from the given string


- int fsync_send_to_char(struct header *head, struct fsync_send* rs, char**pstr);
	To construct a string of bytes from header and fsync_send structures returning the length of the string

- struct fsync_send* fsync_send_from_char(char* str);
	To obtain fsync_send structure from the given string

- int fsync_recv_to_char(struct header *head, struct fsync_recv* rr, char**pstr);
	To construct a string of bytes from header and fsync_recv structures returning the length of the string

- struct fsync_recv* fsync_recv_from_char(char* str);
	To obtain fsync_recv structure from the given string


- int fasync_send_to_char(struct header *head, struct fasync_send* rs, char**pstr);
	To construct a string of bytes from header and fasync_send structures returning the length of the string

- struct fasync_send* fasync_send_from_char(char* str);
	To obtain fasync_send structure from the given string

- int fasync_recv_to_char(struct header *head, struct fasync_recv* rr, char**pstr);
	To construct a string of bytes from header and fasync_recv structures returning the length of the string

- struct fasync_recv* fasync_recv_from_char(char* str);
	To obtain fasync_recv structure from the given string


- int check_media_change_send_to_char(struct header *head, struct check_media_change_send* rs, char**pstr);
	To construct a string of bytes from header and check_media_change_send structures 
	returning the length of the string

- struct check_media_change_send* check_media_change_send_from_char(char* str);
	To obtain check_media_change_send structure from the given string

- int check_media_change_recv_to_char(struct header *head, struct check_media_change_recv* rr, char**pstr);
	To construct a string of bytes from header and check_media_change_recv structures 
	returning the length of the string

- struct check_media_change_recv* check_media_change_recv_from_char(char* str);
	To obtain check_media_change_recv structure from the given string


- int revalidate_send_to_char(struct header *head, struct revalidate_send* rs, char**pstr);
	To construct a string of bytes from header and revalidate_send structures returning 
	the length of the string

- struct revalidate_send* revalidate_send_from_char(char* str);
	To obtain revalidate_send structure from the given string

- int revalidate_recv_to_char(struct header *head, struct revalidate_recv* rr, char**pstr);
	To construct a string of bytes from header and revalidate_recv structures returning 
	the length of the string

- struct revalidate_recv* revalidate_recv_from_char(char* str);
	To obtain revaildate_recv structure from the given string


- int mediactl_send_to_char(struct header *head, struct mediactl_send* rs, char**pstr);
	To construct a string ofbytes from header and mediactl_send structures returning the length of the string

- struct mediactl_send* mediactl_send_from_char(char* str);
	To obtain mediactl_send structure from the given string

- int mediactl_recv_to_char(struct header *head, struct mediactl_recv* rr, char**pstr);
	To construct a string ofbytes from header and mediactl_recv structures returning the length of the string

- struct mediactl_recv* mediactl_recv_from_char(char* str);
	To obtain mediactl_recv structure from the given string


- int message_send_to_char(struct header *head, struct message_send* rs, char**pstr);
	To construct a string of bytes from header and message_send structures returning the length of the string

- struct message_send* message_send_from_char(char* str);
	To obtain message_send structure from the given string


- int request_send_to_char(struct header *head, struct request_send* rs, char**pstr);
	To construct a string of bytes from header and request_send structures returning the length of the string

- struct request_send* request_send_from_char(char* str);
	To obtain request_send structure from the given string

- int request_recv_to_char(struct header *head, struct request_recv* rr, char**pstr);
	To construct a string of bytes from header and request_recv structures returning the length of the string

- struct request_recv* request_recv_from_char(char* str);
	To obtain request_recv structure from the given string

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


////////////////////////////////////////////ioctls.h/////////////////////////////////////////////

The list of ioctls for userdev module:
-------------------------------------

USERDEV_ATTACH 		/*for attaching to userdev*/
USERDEV_DETACH 		/*for detaching from userdev*/
USERDEV_POLL_IN		/*for intimating arrival of input data to poll operation*/
USERDEV_POLL_OUT	/*for intimating arrival of output data to poll operation*/
USERDEV_FASYNC_SIGNAL	/*for intimating asynchronous I/O operation*/
USERDEV_REQUEST_IRQ	/*for requesting for interrupt handling*/
USERDEV_FREE_IRQ	/*for releasing interrupt*/
USERDEV_REQUEST_DMA	/*for requesting for dma channel*/
USERDEV_FREE_DMA	/*for releasing dma channel*/
USERDEV_START_DMA	/*for starting dma*/
USERDEV_CHECK_DMA	/*for checking progress of dma operation*/
USERDEV_ENABLE_DMA	/*for enabling dma operation*/
USERDEV_DISABLE_DMA	/*for disabling dma operation*/
USERDEV_COPY_DMA	/*for copying data from dma buffer to user space*/

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


////////////////////////////////////////////errors.h/////////////////////////////////////////////

List of error codes used in userdev module:
------------------------------------------

Error Code			Value	Reason
---------			-----	------

ERR_INVALID_DEVICE		-19	/*invalid device being referred to*/
ERR_ACCESS_DENIED 	        -1	/*access to device has been denied*/
ERR_MINOR_NOT_FREE 	        -98	/*the minor number requested is not free*/
ERR_INVALID_ID                 -22	/*invalid userdev id*/
ERR_WRONG_PID                  -3	/*wrong process id*/
ERR_TOO_MANY_PROCESSES         -31	/*max number of allowed process reached*/
ERR_INVALID_FILE_DESCRIPTOR    -2	/*the given file descriptor is invalid*/
ERR_DEVICE_NOT_ATTACHED		-6	/*the device being referred to is not attached*/
ERR_PROCESS_DETACHED		-32	/*the driver process being referred to has detached*/
ERR_COMM_FAILURE		-5	/*failure in communication with driver process*/
ERR_MEMORY_FAULT		-14	/*error occured while allocating memory or moving data to/from user*/
ERR_UNKNOWN_IOCTL_COMMAND	-56	/*unknown ioctl command given*/
ERR_FUNC_NOT_SUPPORTED		-38	/*the gven function is not supportted by the device*/
ERR_INVALID_HEADER		-84	/*invalid packet header obtained*/
ERR_INVALID_RECORD		-84	/*invalid attach record has been provided*/
ERR_SIGNAL_RECEIVED		-4	/*interrupted system call*/
ERR_INVALID_DMA			-9	/*dma channel has not been requested for*/

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


//////////////////////////////////////helpers.h // helpers.c/////////////////////////////////////

The functions implemented in this file are:
------------------------------------------

- int get_status(char *fname, struct stat *buf);
	function to obtain the file status information, given the filename of the file
	The return value represents the error code in case of an error or 0 on success

- int get_minor(char *fname);
	function to obtain the minor number of the device from its device filename
	The return value is the minor number incase of a success and -1 incase of an error

- int check_perm_w(char *fname);
	function to check if the given file has write permissions
	The return value is 1, if current user has permissions for the file and 0 otherwise

- int check_perm_x(char *fname);
	function to check if the given file has execute permissions
	The return value is 1, if current user has permissions for the file and 0 otherwise

- struct file *fdtofp(int fd);
	function to convert the given file descriptor into the corresponding file structure
	The return value is NULL incase of an error, and a valid pointer is returned otherwise

- int genID();
	function to generate identification number(request id) for each packet sent by kernel

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


//////////////////////////////////////userdev.h // userdev.c/////////////////////////////////////

userdev filename for Character devices: /dev/userdev_chr
userdev filename for Block devices: 	/dev/userdev_blk

Data structures defined in this file are:
-----------------------------------------

- struct ioctl_element	/*structure to specify ioctl data length*/
	- ioctl number
	- maximum data length

- struct wait_entry	/*structure used to hold information of a pending request*/
	- reason for waking up
	- request id of the request
	- data packet
	- size of the packet
	- wait queue on which to sleep
	- pointer to next wait_entry structure

- struct device_record	/*structure that holds information about each driver process attached*/
	- device name
	- name length
	- type of device, character/ block
	- minor number
	- driver process pid
	- number of ioctls supported
	- array of ioctl_element to specify max data length for each ioctl supported
	- function mask denoting the functions supported
	- read pipe file descriptor
	- pointer to read descriptor's dentry structure
	- pointer to read descriptor's file operations structure
	- write pipe file descriptor
	- pointer to write descriptor's dentry structure
	- pointer to write descriptor's file operations structure
	- pointer to proc file system's root entry for userdev
	- list of pending requests for the driver process
	- read-write lock for accessing pending requests list
	- wait queue for input polling
	- spinlock for accessing input poll queue
	- wait queue for output polling
	- spinlock for accessing output poll queue
	- wait queue for asynchronous i/o notification
	- spinlock for accessing fasync queue
	- read flag, to signify if a read pipe is being actively monitored or not
	- reference count
	- flag to denote if detach is desired
	- irq line number
	- signal number on which to report occurance of an interrupt
	- dma channel number
	- dma buffer
	- dma buffer length
	- spinlock for buffer_head list for block device request


Data structures used in the file:
---------------------------------

- struct device_record *attached_process[MAX_USERDEV_PROCESSES];
	Array of pointers to device records, the array is searched and first free slot is used during attach

- int chr_map[MAX_USERDEV_MINORS];
- int blk_map[MAX_USERDEV_MINORS];
	Maps between minor number and userdevid for character and block devices respectively

- static struct file_operations userdev_fops = {
        owner:  THIS_MODULE,
	read:   userdev_read,
	write:  userdev_write,
	poll:   userdev_poll,
	ioctl:  userdev_ioctl_chr,
	open:   userdev_open_chr,
	flush:  userdev_flush,
	release:userdev_close_chr,
	fsync:  userdev_fsync,
	fasync: userdev_fasync,
	readv:  userdev_readv,
	writev: userdev_writev
	};
	File operations structure for character device driver.

- static struct block_device_operations userdev_bdops = {
        open:                   userdev_open_blk,
        release:                userdev_close_blk,
        ioctl:                  userdev_ioctl_blk,
        check_media_change:     userdev_check_media_change,
        revalidate:             userdev_revalidate,
        mediactl:               userdev_mediactl
	};                   
	File operations structure for block device driver.
	
The functions implemented in this file are:
------------------------------------------

- static void userdev_blkdev_cleanup();
	function to free the block device configuration arrays associated with userdev's major number

- static int userdev_blkdev_init();
	function to initialize block device configuration arrays associated with userdev's major number. The
	function returns 0 on success and -1 on error.
	
- int init_module(void);
	userdev module initialization function that registers a character and a block device driverwith the 
	kernel. The function also initializes the proc file system interface and block device configuration
	arrays and returns 0 on success and an error code incase of a failure.

- void cleanup_module(void);
	userdev module's cleanup function to unregister the character and block device drivers. The function
	uninitializes the proc file system interface and the block device configuration arrays.

- static inline int process_index(int type, int minor);
	function to return the userdev id given the type of device and the minor number

- static inline void addref(struct device_record *devrec);
	function to increment the reference count of a device record

- static void empty_fasync_queue(struct fasync_struct *fas);
	function to empty the asynchronous I/O queue passed as the function argument.

- static void free_device(int id);
	function to free the resources held by an attached driver given the userdev id of the driver.

- static void remref(struct device_record *devrec);
	function to decrement the reference count of the given device record

- static void userdev_interrupt(int irq, void *dev_id, struct pt_regs *regs);
	The interrupt handler for the userdev device driver. The function arguments are the irq line number,
	a pointer to the attached driver's device record set when the driver requests for interrupt handling
	and the list of register values at the time of the interrupt.

- int userdev_message(struct device_record *p, int type, int reqid, int size, void *data);
	userdev function that sends a message packet containing the type of message, request id for 
	sending, data to be sent along with its size, to the driver process whose device record is 
	given. The function returns 0 on success and an error code otherwise.

- ssize_t userdev_read (struct file *filp, char *buffer, size_t size, loff_t *lpos);
	userdev read function to send a read request to the driver process corresponding to the minor 
	number, given the file pointer of the device file, the data buffer address, amount of expected data
	and current position on the device. The function returns the amount of data obtained while the 
	data is copied into the data buffer incase of a success or an error code otherwise.

- ssize_t userdev_write (struct file *filp, const char *buffer, size_t size, loff_t *lpos);
	userdev write function to send a write request to the driver process corresponding to the minor
	number, given the file pointer of the device file, the data to be written, the size and the current 
	position on the device. The function returns the amount of data written on success and an error 
	code otherwise.

- unsigned int userdev_poll (struct file *filp, struct poll_table_struct *polltab);
	userdev poll function to send a poll request to the driver process corresponding to the minor 
	number, given the file pointer of the device file and the poll table. The function returns the
	poll result on success or a poll error otherwise.

- int userdev_ioctl_chr (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr);
	ioctl function for character devices that directs the call to the generic ioctl function passing
	the device type.

- int userdev_ioctl_blk (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr);
	ioctl function for block devices that directs the call to the generic ioctl function passing
	the device type.

- int userdev_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr, int type);
	userdev ioctl function to send a ioctl request to the driver process corresponding to the minor 
	number, given the inode and file pointer of the device file, the ioctl request command, address
	of the data to be sent or received and type of device. The function returns the result of the
	ioctl operation on success or an error code otherwise.

- int userdev_open_chr(struct inode *inode, struct file *filp);
	open function for character devices that directs the call to the generic open function passing
	the type of the device.
	
- int userdev_open_blk(struct inode *inode, struct file *filp);
	open function for block devices that directs the call to the generic open function passing
	the type of the device.
	
- int userdev_open(struct inode *inode, struct file *filp, int type);
	userdev open function to send a open request to the driver process corresponding to the minor 
	number, given the inode and the file pointers for the device file along with the type of the
	device. The function returns the result of the open operation on success or an error code otherwise.

- int userdev_flush (struct file *filp);
	userdev flush function to send a flush request to the driver process corresponding to the minor 
	number, given the file pointer for the device file. The function returns the result of the
	flush operation on success and an error code otherwise.

- int userdev_close_chr(struct inode *inode, struct file *filp);
	close function for character devices that directs the call to the generic close function passing
	the type of the device.
	
- int userdev_close_blk(struct inode *inode, struct file *filp);
	close function for block devices that directs the call to the generic close function passing
	the type of the device.
	
- int userdev_close(struct inode *inode, struct file *filp, int type);
	userdev close function to send a close request to the driver process corresponding to the minor 
	number, given the inode and file pointers of the device file. The function returns the result of
	the close operation on success and an error code otherwise.

- int userdev_fsync (struct file *filp, struct dentry *dentry, int datasync);
	userdev fsync function to send a fsync request to the driver process corresponding to the minor 
	number, given the file pointer of the device file, pointer to the dentry structure of the device
	file and the datasync flag. The function returns the result of the fsync operation on success and
	an error code otherwise.

- int userdev_fasync (int fd, struct file *filp, int on);
	userdev fasync function to send a fasync request to the driver process corresponding to the minor 
	number, given the file descriptor of the device file, file pointer to the device file and a
	flag to denote if an addition or a deletion has to be made. The function returns the result of
	the operation on success and and error code otherwise.

- ssize_t userdev_readv (struct file *filp, const struct iovec *iov, unsigned long num, loff_t *lpos);
	userdev readv function to send a consolidated read request to the driver process corresponding 
	to the minor number, given the file pointer to the device file, a vector of io requests, the
	number of them and the current position on the device. The function returns the total amount of
	data read on success and an error code otherwise.

- ssize_t userdev_writev (struct file *filp, const struct iovec *iov, unsigned long num, loff_t *lpos);
	userdev writev function to send a consolidated write request to the driver process corresponding
	to the minor number, given the file pointer of the device file, a vector of io requests, the
	number of them and the current position on the device. The function returns the total number of
	bytes written incase of success or an error code otherwise.

- int userdev_check_media_change (kdev_t dev);
	userdev check_media_change function to send a check_media_change request to the driver process 
	corresponding to the minor, given the device number of the device. The function returns the
	result of the operation or an error code otherwise.

- int userdev_revalidate (kdev_t dev);
	userdev revalidate function to send a revalidate request to the driver process corresponding to 
	the minor number, given the device number of the device. The function returns the result of the
	operation incase of success and error code otherwise.

- int userdev_mediactl (kdev_t dev, int op, int optarg);
	userdev mediactl function to send a mediactl request to the driver process corresponding to the 
	minor number, given the device number of the device and other optional arguments. The function
	returns the result of the operation incase of success and an error code otherwise.

- void userdev_request(request_queue_t *q);
	userdev request function that handles block device I/O requests from the kernel buffer cache.
	The function takes the request queue prepared by the kernel, sends each request to the 
	corresponding driver process and satisfies each request in the request queue.

- static int attach_device(struct attach_record *att_rec, int minor, int type);
	function to attach a driver process to the userdev kernel driver. It takes the attach record
	obtained from the driver process, the minor number on which to attach the driver process and the
	type of device that the process would handle. The function returns the userdev id of the driver
	process on success or an error code otherwise.

- static int detach_device(struct detach_record *det_rec);
	function to detach the driver process from the userdev framework, given the detach record sent
	by the driver process. The function returns 0.

- static int send(char *packet, int len, struct device_record *p);
	function to send a given packet to the driver process whose device record is given. The length
	of the packet is given in addition to the above 2 arguments. The function returns the number of
	bytes sent on success or an error code otherwise.

- static void add_wait_entry(struct device_record *rec, struct wait_entry *entry);
	function that adds the given wait_entry structure to the pending list queue of the given device 
	record.

- static void del_wait_entry(struct device_record *rec, struct wait_entry *entry);
	function to remove the given wait_entry structure from the pending list queue of the given device 
	record.

- static void userdev_wake_up_one(int id, char *pkt, int size, int why, struct device_record *rec);
	function to wake up a sleeping process given the id of the request its been waiting for, a pointer
	where the received packet needs to given to it, the size of the data recevied, the reason for
	waking it up and the device record in whose pending list the process had been placed.

- static void userdev_wake_up_first(struct device_record* rec);
	function to wake up the first process on the given device record's pending list.

- static void userdev_wake_up_all(struct device_record *rec);
	function to wakeup all sleeping processes on the given device record's waiting list.

- static int receive(struct header *head, char **packet, struct device_record *p, int id);
	function to receive the header and data fields of the response packet in the first two arguments
	given the device record of the driver process from which the packet has to be received and the
	id of the request for which response is sought. The function returns the number of bytes received
	on success and an error code otherwise.

- static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent,
                                int (*readproc)(char*, char**, off_t, int, int*, void*), void *ptr);
	function to create a new proc file system node with the given name, mode, under the given parent
	directory. A pointer to the function to be called when the file corresponding to the node created
	in the proc file system is opened and the data to be passed form the rest of the arguments.
	The function returns a pointer to the proc file system entry created incase of a success or a NULL
	pointer otherwise.

- static void del_proc_entry(char *name, struct proc_dir_entry *parent);
	function to delete the proc file system node with the given name under the given parent directory.

- int userdev_proc_init();
	function to initialize the proc file system interface for the userdev kernel driver.

- void userdev_proc_cleanup();
	function to cleanup and remove the nodes created by the userdev kernel driver in the proc file system.

- static int device_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
	The function that is called when the proc file system node created for each attached driver process is
	opened for reading. The function passes the pointer to a string into which all the data that needs
	to be displayed needs to be appended. The return value is the number of bytes written to the string.

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