NEXT UP previous
Next: Special Control Functions

Basic Entry Points

Having now seen how to register your character device drivers with the kernel, and how to allocate memory for use within a driver we now turn our attention to the main entry points into the driver, whose addresses will appear in the driver's file_operations structure.

The main system calls for use with a character device or file are: open(), close(), read(), write() and ioctl(). Each of these system calls, when it has been established that they are intended for your driver, can result in the appropriate driver function being executed.

open Function

The driver's open() function is called when a user process executes an openo system call on a device special file associated with this driver. The open() function has the prototype:

	int open(struct mode *inode, struct file *file);

where the mode parameter is a pointer to the mode structure of the device special file which was accessed, and file is a pointer to the file structure for this device.

Typical actions for the openO function are:

release Function

The driver's release() function is only called when the last user process which has the device open close()s it:

	void release(struct mode *inode, struct file *file);

where inode is a pointer to the mode structure of the device special file, and file is a pointer to the file structure for this device.

The release() function may be required to:

read Function

The driver's read() function is called whenever a read() system call is executed on a device special file associated with this character device driver:

	void read(struct mode *inode, struct file *file,
			char *buf, mt count);

where mode is a pointer to the mode structure of the device special file, file is a pointer to the file structure for this device, buf is a pointer to a buffer in user space whose address was passed by the user process into the read() system call, and count is the number of bytes required by the user process (also passed into the read() system call).

The function of read() is to copy up to count bytes of data from the hardware device (if present) or allocated kernel memory, out to the buffer in user memory given by buf.

Care must be taken when performing the copy as the pointer in buf is an address located in user memory, not an address which can be used directly from within the kernel. In order to perform the copy correctly you should use one of several special copy functions provided specifically for this purpose. They are defined in <asm/segment.h>:

	void put_user_byte(char data_byte, char *u_addr); 
	void put_user_word(short data_word, short *u_addr); 
	void put user_long(long data_long, long *u_addr);
	void memcpy_tofs(void *u_addr, void *k_addr  unsigned long cnt);

where, for the put_user...() functions, data_byte, data_word and data_long are the char, short and long values, respectively, to be copied into user space at address n_addr and, for memcpy_tofs(), cnt bytes will be copied from kernel space address k_addr to user space address u_addr.

For backward compatibility with older kernels (older is up to and including early 0.99), three other functions are provided:

	void put_fs_byte(char data_byte, char *u_addr); 
	void put_fs_word(short data_word, short *u_addr); 
	void put_fs_long(long data_long, long *u_addr);

but these are exactly equivalent to the three previous put_user_XXX functions. Additionally, if you are careful with your pointer types, all of these function calls can be replaced in newer kernels (newer is 1.3 and beyond) with a single call:

	void put_user(type value, type *u_addr);

This call is implemented as a macro in <asm/segment.h>, whose expansion depends on the data type of the pointer parameter u_addr. The type of object that the u_addr pointer is pointing to is used to determine how many bytes of value to transfer to user memory space.

Before you use any of these functions to write into user memory, however, it is important for you to verify that the address in user space passed to you by the user process actually points to an area of memory to which the user has write access. This is done with the kernel's verify_area() function:

	#include <linux/min.h>

	int verify_area(int access, void *u_addr, unsigned long size);

where access is one of VERIFY_WRITE or VERIFY_READ depending on whether you wish to write to or read from the user memory, u_addr is the start address in user space and size is the size in bytes of the memory block to be accessed.

The verify_area() function returns the value zero if the specified access is permitted or -EFAULT on error.

write Function

The write() function in the driver is called whenever a write() system call is performed on a device special file belonging to this driver:

	void write(struct mode *inode, struct file *file, char *buf, int count);

where mode is a pointer to the special file's mode structure, file is a pointer to the file structure, buf is a pointer to a buffer in user space, passed into the write() system call, from which user characters will be written, and count is the number of bytes to transfer (also passed into the writeO system call).

The purpose of the device driver's write() function is to copy up to count bytes of data from the buffer buf in user space out to the hardware (if applicable) or the internal buffer.

As with read(), the value in buf is an address in user space and so, again, one of the following special functions should be used to perform the copy operation:

	unsigned char get_user_byte(char *u_addr); 
	unsigned short get_user_word(short *u_addr); 
	unsigned long get_user_long(long *u_addr);
	void memcpy_fromfs(void *k_addr, void *u_addr, unsigned long cnt);

where the get_user... () functions return the char, short or long value contained at the user space address u_addr and for memcpy_fromfs(), cnt bytes will be copied to kernel space address k_addr from user space address u_addr.

Again, for backward compatibility with older kernels, three other functions are provided:

	unsigned char get_fs_byte(char *u_addr);
	unsigned short getfs_word(short *u_addr); 
	unsigned long get_fs_long(long *u_addr);

that are equivalent to the three previous get_user_XXX functions. Newer kernels also support the single call:

	type get_user(type *u_addr);

which is implemented as a macro in <asm/segment.h whose expansion depends on the data type of the pointer parameter u_addr. The type of object that the u_addr pointer is pointing to is used to determine how many bytes of data to transfer from user memory space and also the return type of the get_user() call itself.

Just as in the case of the device driver's read() function, you should arrange to call the verify_area() function before using any of the previous functions to read from user memory.


NEXT UP previous
Next: Special Control Functions