Friday, 20 July 2018

Cdev structure and File Operations of Character drivers

In linux kernel struct inode structure is used to represent files. Therefore, it is different from the file structure that represents an open file descriptor. 
The inode structure contains a great deal of information about the file.
struct cdev is one of the elements of the inode structure. As you probably may know already, an inode structure is used by the kernel internally to represent files.


The struct cdev is the kernel’s internal structure that represents char devices.


struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

here are two ways of allocating and initializing one of these structures.
  1. Runtime Allocation
  2. Own allocation
If you wish to obtain a standalone cdev structure at runtime, you may do so with code such as:
struct cdev *my_cdev = cdev_alloc( );
my_cdev->ops = &my_fops;
Or else you can embed the cdev structure within a device-specific structure of your own by using below function.
void cdev_init(struct cdev *cdev, struct file_operations *fops);
nt cdev_add(struct cdev *dev, dev_t num, unsigned int count);
Here,
dev is the cdev structure,
num is the first device number to which this device responds, and
count is the number of device numbers that should be associated with the device. Often count is one, but there are situations where it makes sense to have more than one device number correspond to a specific device.
If this function returns negative error code, your device has not been added to the system. So check the return value of this function.
After a call to cdev_add(), your device is immediately alive. All functions you defined (through the file_operations structure) can be called.
To remove a char device from the system, call:
void cdev_del(struct cdev *dev);

File_Operations

The file_operations structure is how a char driver sets up this connection. The structure, defined in , is a collection of function pointers. Each open file  is associated with its own set of functions .
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
  loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
};


This file_operations structure contains many fields. But we will concentrate on very basic functions.


ssize_t (*read) (struct file *, char _ _user *, size_t, loff_t *);
Used to retrieve data from the device. A null pointer in this position causes the read system call to fail with -EINVAL (“Invalid argument”). A non negative return value represents the number of bytes successfully read


int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
The ioctl system call offers a way to issue device-specific commands (such as formatting a track of a floppy disk, which is neither reading nor writing). Additionally, a few ioctl commands are recognized by the kernel without referring to the fops table. If the device doesn’t provide an ioctl method, the system call returns an error for any request that isn’t predefined (-ENOTTY, “No such ioctl for device”).
int (*open) (struct inode *, struct file *);
Though this is always the first operation performed on the device file, the driver is not required to declare a corresponding method. If this entry is NULL, opening the device always succeeds, but your driver isn’t notified.
int (*release) (struct inode *, struct file *);
This operation is invoked when the file structure is being released. Like openrelease can be NULL.

  1. Build the driver by using Makefile (sudo make)
  2. Load the driver using sudo insmod
  3. Do echo 1 > /dev/etx_device
Echo will open the driver and write 1 into the driver and finally close the driver. So if i do echo to our driver, it should call the open, write and release functions. Just check.
linux@embetronicx-VirtualBox:/home/driver/driver#  echo 1 > /dev/etx_device 
4. Now Check using dmesg
linux@embetronicx-VirtualBox:/home/driver/driver$ dmesg
[19721.611967] Major = 246 Minor = 0
[19721.618716] Device Driver Insert...Done!!!
[19763.176347] Driver Open Function Called...!!!
[19763.176363] Driver Write Function Called...!!!
[19763.176369] Driver Release Function Called...!!!
5. Do cat > /dev/etx_device
Cat command will open the driver, read the driver and close the driver. So if i do cat to our driver, it should call the open, read and release functions. Just check.
linux@embetronicx-VirtualBox:/home/driver/driver#  cat /dev/etx_device 
6. Now Check using dmesg
linux@embetronicx-VirtualBox:/home/driver/driver$ dmesg
[19763.176347] Driver Open Function Called...!!!
[19763.176363] Driver Read Function Called...!!!
[19763.176369] Driver Release Function Called...!!!
7. Unload the driver using sudo rmmod




















No comments:

Post a Comment