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.
- Runtime Allocation
- 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, andcount
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 open, release can be NULL.
#include
#include
#include
#include
#include
#include
#include
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.release = etx_release,
};
static int etx_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Driver Open Function Called...!!!\n");
return 0;
}
static int etx_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Driver Release Function Called...!!!\n");
return 0;
}
static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver Read Function Called...!!!\n");
return 0;
}
static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
printk(KERN_INFO "Driver Write Function Called...!!!\n");
return len;
}
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
printk(KERN_INFO "Cannot allocate major number\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
etx_cdev.owner = THIS_MODULE;
etx_cdev.ops = &fops;
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
printk(KERN_INFO "Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
printk(KERN_INFO "Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
printk(KERN_INFO "Cannot create the Device 1\n");
goto r_device;
}
printk(KERN_INFO "Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
void __exit etx_driver_exit(void)
{
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX
MODULE_DESCRIPTION("A simple device driver");
MODULE_VERSION("1.3");
|
- Build the driver by using Makefile (
sudo make
) - Load the driver using
sudo insmod
- 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