Tuesday, July 31, 2007

Character Device Operation

We basically do two simple things with devices, read and write. To implement a character device, we need to open the device and then close it when we re done. Linux treats devices as file systems. We do create a file, read/write in it then close the file in C/C++. We have to do the same here for devices.

NOTE: The texts below are copied from an unknown source, if anyone know it's sources, let me know - I'll acknowledge them.

This blog is for personal study purpose only and do not have any affiliation with any group or company.

Opening a char device

A character device driver is first accessed by executing its open() function.
− This function is executed in the sys_open() system call.
− The open() function will just not be called if it is not implemented by a specific device driver. This behavior may be observed in dentry_open().

In the case that a character needs to implement its open() function, it will have to provide the following
functionality:
− Increment the usage count, so that the driver may not be removed from the Kernel (in the case of a module).
− Check for hardware-specific problems associated with this particular device.
− Initialize the hardware, if it is needed.
− Identify the minor number of the device that was open and update the f_op pointer if necessary. This is needed for device sharing the same major number but having different device drivers (i.e., miscellaneous devices).
− Allocate the memory needed for the various data structures used in the device driver and initialize these structures.

Reading characters from device

• read() is called to read data from the device. The form of the read function is as follows:
static ssize_t device_read(struct file * file,
char * buffer, size_t count, loff_t *ppos)
• Note that the arguments of the read() method have changed in the 2.2 and subsequent Kernels.
− The new method passes only a file structure, from which we can find the disk inode associated with the device file.
• The read() method implemented by a device driver should copy the specified number of bytes into the
buffer and return the actual number of bytes read (or an error code).
− The read() function may therefore read less data than was requested. In this case the returned value will be less than size passed in parameter.
− A negative return value means that there was an error.
− Note that the buffer field passed to the device drivers refers to the memory space of the User space process that invoked the read() system call.
− The copy_to_user() function must thus be used in order to return the data in the proper memory segment.
• The following is an example of the structure used in a simple read() method. It was taken from the PC Watchdog driver.
#define TEMP_MINOR 131
static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
loff_t *ppos)
{
unsigned short c;
unsigned char cp;
/* Can't seek on this device, so return an error */
/* if the offset is not the same as current reading position */
if (ppos != &file->f_pos)
return -ESPIPE;
/* Find the minor number associated with the device file*/
switch(MINOR(file->f_dentry->d_inode->i_rdev))
{
/* Go ahead if this is the device we want to handle */
case TEMP_MINOR:
/*
* Convert metric to Fahrenheit, since this was
* the decided 'standard' for the return value.
*/
c = inb(current_readport);
cp = (c * 9 / 5) + 32;
/* Copy the result in User space */
if(copy_to_user(buf, &cp, 1))
return -EFAULT;
/* Only one byte can be read, thus return 1 */
return 1;
/* Return error if wrong minor number */
default:
return -EINVAL;
}
}


Writing to a character device

• The write() method implemented by a driver is invoked to write data to the device. It is defined as
follows:
static ssize_t device_write(struct file *file,
const char *buf, size_t count, loff_t *ppos)
• write() should copy the specified number of bytes from the User space buffer into the device.
− The number of bytes specified by the value of count should be written to the device.
− Similarly to the read() method, the number returned by the write() function should match the value of count. If it’s not the case, the data was partially written or, in the case of a negative value, an error occurred.
• The following is an example of the implementation of a write() function. It does not address hardware issues, which we will reserve for the next chapters.
static ssize_t pcwd_write(struct file *file, const char *buf, size_t
len, loff_t *ppos)
{
/* We can't seek on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
if (len)
{
pcwd_send_heartbeat();
return 1;
}
return 0;
}



Closing a char device

A character device driver is closed when a User space application no longer needs it.
− This function is executed in the sys_close() system call.
− The release() function will not be called if it is not implemented by a specific device driver. This behavior may be observed in fput().
• The release() function is in charge of the following steps:
− It decrements the usage count. This is necessary in order for the Kernel to be able to remove the module.
− Removes any unnecessary data from memory. This is particularly true for data placed in the private_data field of the file structure associated with the device.
− Shut down the physical device if needed. This includes any operation that must be executed in order to leave the hardware in a sane state, and disabling interrupts.
• The release() function associated with a particular driver will not be invoked if the open() function was not called.
− Again, this may be observed in the fput() function.

3 comments:

Anonymous said...

Nice brief and this enter helped me alot in my college assignement. Say thank you you as your information.

Anonymous said...

thanks for this nice post 111213

Anonymous said...

thanks for this tips