diff --git a/ioctl/Makefile b/ioctl/Makefile new file mode 100644 index 0000000..1e87598 --- /dev/null +++ b/ioctl/Makefile @@ -0,0 +1,7 @@ +obj-m += demo_ioctl_driver.o + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean \ No newline at end of file diff --git a/ioctl/demo_ioctl_driver.c b/ioctl/demo_ioctl_driver.c new file mode 100644 index 0000000..e42d611 --- /dev/null +++ b/ioctl/demo_ioctl_driver.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include +#include //kmalloc() +#include //copy_to/from_user() +#include +#include "ioctl_const.h" + +int32_t value = 0; + +dev_t dev = 0; +static struct cdev demo_ioctl_driver_cdev; +/** + * Function Prototypes + */ +static int __init demo_ioctl_driver_driver_init(void); +static void __exit demo_ioctl_driver_driver_exit(void); +static int demo_ioctl_driver_open(struct inode *inode, struct file *file); +static int demo_ioctl_driver_release(struct inode *inode, struct file *file); +static ssize_t demo_ioctl_driver_read(struct file *filp, char __user *buf, size_t len, loff_t *off); +static ssize_t demo_ioctl_driver_write(struct file *filp, const char __user *buf, size_t len, loff_t *off); +static long demo_ioctl_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + +/** + * __user is a macro in Linux kernel that indicates that + * the pointer is pointing to user-space memory. It is + * used for safety and correctness when writing kernel code + * that interacts with user-space. + * + * -> This annotation helps identify user-space pointer + * in kernel code. + * -> It is mainly used for static analysis and runtime + * safety checks. + * + */ + + +/** + * File operation structure + */ +static struct file_operations fops = { + .owner = THIS_MODULE, + .read = demo_ioctl_driver_read, + .write = demo_ioctl_driver_write, + .open = demo_ioctl_driver_open, + .unlocked_ioctl = demo_ioctl_driver_ioctl, + .release = demo_ioctl_driver_release +}; + +/** + * This function will be called when we open the Device file + */ +static int demo_ioctl_driver_open(struct inode *inode, struct file *file){ + pr_info("Device File Opened...!!!\n"); // Same functionality as printk + return 0; +} + +/** + * This Function will be called when we close the Device file + */ +static int demo_ioctl_driver_release(struct inode *inode, struct file *file){ + pr_info("Device File Closed...!!!\n"); + return 0; +} + +/** + * This function will be called when read the Device file + */ +static ssize_t demo_ioctl_driver_read(struct file *filp, char __user *buf, size_t len, + loff_t *off){ + pr_info("Read Function\n"); + return 0; +} + +/** + * This function will be called when we write the Device file + */ +static ssize_t demo_ioctl_driver_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){ + pr_info("Write fucntion\n"); + return 0; +} + +/** + * This function will be called when we write IOCTL on the Device file + */ +static long demo_ioctl_driver_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ + switch(cmd){ + case WR_VALUE: + /*utility function by kernel to copy from/to between user and kernel space*/ + if( copy_from_user(&value, (int32_t*) arg, sizeof(value)) ){ + pr_err("Data Write . Err!\n"); + } + pr_info("demo_ioctl_driver_ioctl :: WR_VALUE called\n" ); + pr_info("Value = %d\n", value); + break; + case RD_VALUE: + if( copy_to_user((int32_t *)arg, &value, sizeof(value)) ){ + pr_err("Data Read : Err\n"); + } + pr_info("demo ioctl driver ioctl :: RD_VALUE called\n"); + break; + default: + pr_info("Default\n"); + break; + } + return 0L; +} + + +/** + * Module Init function + */ + +static int __init demo_ioctl_driver_driver_init(void){ + + //this alloc_chrdev_region will acquire + //major number dynamically. + if(alloc_chrdev_region(&dev, 0, 1, "demo_ioctl_driver_Dev") < 0){ + pr_err("Cannot allocate major number\n"); + return -1; + } + pr_info("Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev)); + + /*Creating cdev structure*/ + cdev_init(&demo_ioctl_driver_cdev, &fops); + + //cdev_add is used to add character device + //to character device list. + int ret = cdev_add(&demo_ioctl_driver_cdev, dev, 1); + if( ret < 0 ){ + pr_info("Error registering device driver\n"); + cdev_del( &demo_ioctl_driver_cdev ); + unregister_chrdev_region(dev, 1); + return -1; + } + pr_info("\nDevice Registered: demo_ioctl_driver_Dev\n" ); + + return 0; +} + +/** + * Module exit function + */ + +static void __exit demo_ioctl_driver_driver_exit(void){ + cdev_del( &demo_ioctl_driver_cdev); + unregister_chrdev_region(dev, 1); + pr_info("Device Driver Remove....Done!!!\n"); +} + +module_init(demo_ioctl_driver_driver_init); +module_exit(demo_ioctl_driver_driver_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Junet Hossain"); +MODULE_DESCRIPTION("Simple Linux device driver (IOCTL)"); \ No newline at end of file diff --git a/ioctl/ioctl_app b/ioctl/ioctl_app new file mode 100755 index 0000000..27b4886 Binary files /dev/null and b/ioctl/ioctl_app differ diff --git a/ioctl/ioctl_app.c b/ioctl/ioctl_app.c new file mode 100644 index 0000000..1a80844 --- /dev/null +++ b/ioctl/ioctl_app.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ioctl_const.h" + +int main(){ + + int fd; + int32_t value , number; + + printf("\nOpening Driver\n"); + fd = open("/dev/demo_ioctl_device", O_RDWR); + if( fd < 0 ){ + printf("Cannot open device file...\n"); + return 0; + } + + printf("Enter the Value to send\n"); + scanf("%d", &number); + printf("Writing Value to Driver\n"); + ioctl(fd, WR_VALUE, (int32_t*) &number); + + printf("Reading Value from Driver\n"); + ioctl(fd, RD_VALUE, (int32_t*) &value); + printf("Value is %d\n", value); + + printf("Closing Driver\n"); + close(fd); + return 0; +} \ No newline at end of file diff --git a/ioctl/ioctl_const.h b/ioctl/ioctl_const.h new file mode 100644 index 0000000..8569434 --- /dev/null +++ b/ioctl/ioctl_const.h @@ -0,0 +1,48 @@ +#ifndef _IOCTL_CHAR_DEVICE_H_ +#define _IOCTL_CHAR_DEVICE_H_ + +#include + +/** + * ioctl constants are defined in this header file for our + * char_driver_ioctl driver. + * + * This file explains how the constants are defined with + * the use of inline comments + */ + +/** + * Constants have to be globally unique. Though this is not + * compulsory, it is necessary. This is because of the simple + * reason: we do not want our commands to clash with the commands + * in other drivers. we don't want the right command doing to + * the wrong driver or vice-versa + */ + +/** + * We have to base our constants based on a magic number. + */ + +#define DEMO_IOCTL_MAGIC 'a' + +/** + * Defining constants requires us to decide following values + * + * 1. type or magic number (type) + * 2. sequence number which is eight bits wide. This means + * we can have up to 256 ioctl commands (nr) + * 3. direction if the arg is involed, whether we are reading + * or writing + * 4. size(composition) of the argument. + * + * To arrive at unique numbers easily we use the following macros + * _IO(type, nr); -> no data is passed from kernel to user space, just like void function + * _IOW(type, nr, dataitem) the fourth item calculated using sizeof -> to read from user to kernel + * _IOR(type, nr, dataitem) -> to write to user from kernel + * _IOWR(type, nr, dataitem) -> to do both read and write + */ + +#define WR_VALUE _IOW(DEMO_IOCTL_MAGIC, 1, int32_t *) +#define RD_VALUE _IOR(DEMO_IOCTL_MAGIC, 2, int32_t *) + +#endif \ No newline at end of file