Created
September 20, 2013 03:44
-
-
Save gauthamkantharaju/6633042 to your computer and use it in GitHub Desktop.
Simple character device driver with debugfs entry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "hello.h" | |
struct dentry *parent = NULL; | |
u32 test_db = 0; | |
CNTXT *db_struct = NULL; | |
char wrbuf[sizeof(CNTXT) * 5]; | |
static | |
ssize_t db_read(struct file *filp, char __user *buf, size_t count,loff_t *fpos) | |
{ | |
CNTXT *data = (CNTXT *) filp->f_dentry->d_inode->i_private; | |
char rdbuf[sizeof(CNTXT) * 5]; | |
snprintf(rdbuf,(sizeof(CNTXT) * 3), | |
"Data: buf:%s \n arry:%ld \n len:%d \n rindex:%d \n windex:%d \n", | |
data->buf, (long)data->arry, data->len, data->rindex, | |
data->windex); | |
return simple_read_from_buffer(buf, (sizeof(CNTXT) * 3), fpos, rdbuf, | |
(sizeof(CNTXT) * 3)); | |
} | |
#if 0 | |
static ssize_t db_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos) | |
{ | |
return simple_write_to_buffer(wrbuf, sizeof(CNTXT), fpos, buf, sizeof(CNTXT)); | |
} | |
#endif | |
static struct file_operations db_fops = { | |
.read = db_read, | |
}; | |
#if 0 | |
static struct file_operations db_fops1 = { | |
.write = db_write, | |
}; | |
#endif | |
struct dentry *debugfs_create(void) | |
{ | |
struct dentry *file = NULL; | |
parent = debugfs_create_dir(MODULE_NAME, NULL); | |
if(!parent) | |
{ | |
dmsg(KERN_ALERT" Failed to create debugfs directory \n"); | |
parent = NULL; | |
} | |
else | |
{ | |
file = debugfs_create_u32("db_test", 0666, parent, &test_db); | |
if(!file) | |
{ | |
dmsg(KERN_ALERT | |
"Failed to create debugfs file under parent directory \n"); | |
file = NULL; | |
} | |
db_struct = (CNTXT *) kmalloc(sizeof(char) * sizeof(CNTXT), | |
GFP_KERNEL); | |
if( !db_struct ) | |
{ | |
dmsg("Dynamic memory allocation failed \n"); | |
db_struct = NULL; | |
} | |
db_struct->buf = "Initial value"; | |
db_struct->arry = &test_db; | |
db_struct->len = 1; | |
db_struct->rindex = 1; | |
db_struct->windex = 1; | |
debugfs_create_file("db_struct", 0666, parent, db_struct, &db_fops); | |
} | |
#if 0 | |
debugfs_create_file("db_struct1", 0666, parent, wrbuf, &db_fops1); | |
#endif | |
return parent; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Module version : 1.0 | |
Author : Gautham Kantharaju | |
Description : Simple device driver with debugfs entry | |
*/ | |
#include "hello.h" | |
static int majorno = 0; | |
/* Non-Static variable */ | |
int len = 5; | |
atomic_t flag = ATOMIC_INIT(0); | |
int x = 1; | |
module_param(x, bool, 0); | |
MODULE_PARM_DESC(x, "Boolean value, default is 1"); | |
static char *str = "Simple Hello Device driver"; | |
module_param(str, charp, 0); | |
MODULE_PARM_DESC(str, "Character pointer, default string : Simple Hello Device Driver"); | |
static int arr[MAX_LEN]; | |
static int ptr; | |
module_param_array(arr, int, &ptr, 0); | |
MODULE_PARM_DESC(arr, "Integer array, have to be populated"); | |
static char string[MAX_STRLEN]; | |
module_param_string(s, string, MAX_STRLEN, 0); | |
struct dentry *debugfs_create(void); | |
struct dentry *db_parent; | |
extern u32 test_db; | |
extern CNTXT *db_struct; | |
CNTXT *test = NULL; | |
wait_queue_head_t myqueue; | |
static int minor_open(struct inode *inode, struct file *filp) | |
{ | |
int res = 0; | |
test = kmalloc(sizeof(char) * sizeof(CNTXT), GFP_KERNEL); | |
if( !test ) | |
{ | |
dmsg("Dynamic memory allocation failed \n"); | |
res = -1; | |
} | |
else | |
{ | |
test->buf = str; | |
test->arry = arr; | |
test->len = strlen(str); | |
test->windex = 0; | |
test->rindex = 0; | |
spin_lock_init(&test->lock); | |
filp->private_data = kmalloc(sizeof(char) * sizeof(CNTXT), GFP_KERNEL); | |
if( !filp->private_data ) | |
{ | |
dmsg("Dynamic memory allocation failed \n"); | |
res = -1; | |
} | |
} | |
return res; | |
} | |
static ssize_t minor_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) | |
{ | |
unsigned long flags; | |
spin_lock_irqsave(&test->lock, flags); | |
copy_from_user(test->buf, buf, count); | |
test->buf[count] = '\0'; | |
test->len = count; | |
test->windex = count; | |
*((CNTXT *) filp->private_data) = *((CNTXT *) test); | |
*((CNTXT *) db_struct) = *((CNTXT *) test); | |
spin_unlock_irqrestore(&test->lock, flags); | |
atomic_set(&flag, 1); | |
wake_up_interruptible(&myqueue); | |
return count; | |
} | |
static ssize_t minor_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |
{ | |
unsigned long flags; | |
unsigned int rem = 0; | |
if(count > test->len) | |
count = test->len; | |
if(filp->f_flags & O_NONBLOCK) | |
return -EAGAIN; | |
if(0 == atomic_read(&flag)) | |
{ | |
if(wait_event_interruptible(myqueue, (atomic_read(&flag) == 1))) | |
return -ERESTARTSYS; | |
} | |
spin_lock_irqsave(&test->lock, flags); | |
test->rindex = count; | |
*((CNTXT *) filp->private_data) = *((CNTXT *) test); | |
test_db = test->rindex; | |
dmsg("Read Index:%u \t debug:%u remainder:%u \n", test->rindex, test_db, rem); | |
rem = copy_to_user(buf, test->buf, count); | |
spin_unlock_irqrestore(&test->lock, flags); | |
return count; | |
} | |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) | |
static long minor_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
#else | |
static int minor_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) | |
#endif | |
{ | |
int ret = 0; | |
int res = 0; | |
if(_IOC_TYPE(cmd) != IOCTL_HELLO_MAGIC) | |
return -ENOTTY; | |
if(_IOC_NR(cmd) > IOCTL_MAX_CMDS) | |
return -ENOTTY; | |
if(_IOC_DIR(cmd) & _IOC_READ) | |
res = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); | |
if(_IOC_DIR(cmd) & _IOC_WRITE) | |
res = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); | |
if(res) | |
return -EFAULT; | |
switch(cmd) | |
{ | |
case IOCTL_HELLO_TEST1: | |
dmsg("In IOCTL test 1 \n"); | |
break; | |
case IOCTL_HELLO_TEST2: | |
dmsg("In IOCTL test 2 \n"); | |
break; | |
default: | |
dmsg("In IOCTL default case \n"); | |
break; | |
} | |
return ret; | |
} | |
static int minor_close(struct inode *inode, struct file *filp) | |
{ | |
int res = 0; | |
int index = 0; | |
CNTXT result; | |
result = *((CNTXT *) filp->private_data); | |
#if 0 | |
dmsg("str:%s \t len:%d \t rindex:%d \t windex:%d \n", *((long *) filp->private_data), | |
*(unsigned int *) (filp->private_data + 2), *(unsigned int *) (filp->private_data + 3), | |
*(unsigned int *) (filp->private_data + 4)); | |
dmsg("len addr:%p \t rindex:%p \n", (long *)(filp->private_data + 2), (long *)(filp->private_data + 3)); | |
dmsg("arr:%ld \t test:%ld \t arry:%ld \n", (long) arr, (long) test->arry, | |
*((long *) filp->private_data + 1)); | |
for(index=0; index < MAX_LEN; index++) | |
dmsg("arr[%d]: %d \n", index, *((unsigned int *)(*((long *)filp->private_data + 1)) + index)); | |
#endif | |
dmsg("String:%s \t len:%u \t rindex:%u \t windex:%u \n", result.buf, | |
result.len, result.rindex, result.windex); | |
for(index=0; index < MAX_LEN; index++) | |
dmsg("arr[%d]: %d \n", index, result.arry[index]); | |
kfree(test); | |
kfree(filp->private_data); | |
kfree(db_struct); | |
return res; | |
} | |
static struct file_operations hello_minor_fops = { | |
.owner = THIS_MODULE, | |
.open = minor_open, | |
.release = minor_close, | |
.write = minor_write, | |
.read = minor_read, | |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) | |
.unlocked_ioctl = minor_ioctl | |
#else | |
.ioctl = minor_ioctl | |
#endif | |
}; | |
static int hello_open(struct inode *inode, struct file *filp) | |
{ | |
int res = 0; | |
dmsg("Minor no specific file operations: %d \n", iminor(inode)); | |
switch(iminor(inode)) | |
{ | |
case 0: | |
filp->f_op = &hello_minor_fops; | |
break; | |
default: | |
dmsg("There is no minor no with this value : %d \n", | |
iminor(inode)); | |
break; | |
} | |
if(filp->f_op && filp->f_op->open) | |
res = filp->f_op->open(inode, filp); | |
else | |
res = 0; | |
return res; | |
} | |
static struct file_operations hello_fops = { | |
.owner = THIS_MODULE, | |
.open = hello_open | |
}; | |
static int __init hello_init_module(void) | |
{ | |
int major = 0; | |
int res = SUCCESS; | |
int index = 0; | |
int len = 0; | |
for(index=0; index < MAX_LEN; index++) | |
dmsg("arr[%d]:%d \n", index, arr[index]); | |
dmsg("Module param str:%s \n", str); | |
len = strlen(string); | |
for(index=0; index < len; index++) | |
dmsg("string[%d]:%c \n", index, string[index]); | |
iline(string); | |
major = register_chrdev(majorno, MODULE_NAME, &hello_fops); | |
if(major < 0) | |
dmsg("Failed to register hello driver \n"); | |
else | |
{ | |
dmsg("Driver registered, majorno:%d \n", major); | |
if(0 == majorno) | |
majorno = major; | |
init_waitqueue_head(&myqueue); | |
db_parent = debugfs_create(); | |
} | |
return res; | |
} | |
int test_export(void) | |
{ | |
int ret = 0; | |
dmsg("Testing \n"); | |
ret = 1; | |
return ret; | |
} | |
#if 0 | |
EXPORT_SYMBOL(test_export); | |
EXPORT_SYMBOL(len); | |
#endif | |
static void __exit hello_cleanup_module(void) | |
{ | |
debugfs_remove_recursive(db_parent); | |
unregister_chrdev(majorno, MODULE_NAME); | |
dmsg("Exiting from the kernel \n"); | |
} | |
module_init(hello_init_module); | |
module_exit(hello_cleanup_module); | |
MODULE_LICENSE("GPL"); | |
MODULE_DESCRIPTION("Simple Hello driver module with debug information"); | |
MODULE_VERSION(MOD_VERSION); | |
MODULE_AUTHOR("Gautham Kantharaju <[email protected]>"); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _HELLO_H_ | |
#define _HELLO_H_ | |
#include <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/fs.h> | |
#include <linux/slab.h> | |
#include <linux/version.h> | |
#include <linux/kernel.h> | |
#include <linux/sched.h> | |
#include <linux/debugfs.h> | |
#include <linux/spinlock.h> | |
#include <linux/wait.h> | |
#include <linux/ioctl.h> | |
#include <linux/moduleparam.h> | |
#include <asm/atomic.h> | |
#define CONFIG_UACCESS 1 | |
#if (CONFIG_UACCESS == 1) | |
# include <asm/uaccess.h> | |
#else | |
# error "Cannot include uaccess" | |
#endif | |
#define dmsg(string, args...) \ | |
printk(KERN_DEBUG "%s:%s:%d:" string, MODULE_NAME, __FUNCTION__, __LINE__, ##args) | |
#define SUCCESS 0 | |
#define ERROR -1 | |
#define MAX_STRLEN 50 | |
#define MAX_LEN 5 | |
#define MODULE_NAME "hello" | |
#define MOD_VERSION "1.0" | |
#define IOCTL_MAX_CMDS 2 | |
#define IOCTL_HELLO_MAGIC 'h' | |
#define IOCTL_HELLO_TEST1 _IO(IOCTL_HELLO_MAGIC, 0) | |
#define IOCTL_HELLO_TEST2 _IO(IOCTL_HELLO_MAGIC, 1) | |
#if 1 | |
static inline void iline(char *str) | |
{ | |
dmsg("String:%s \n", str); | |
} | |
#endif | |
typedef struct _context | |
{ | |
char *buf; | |
int *arry; | |
unsigned int len; | |
unsigned int rindex; | |
unsigned int windex; | |
spinlock_t lock; | |
}CNTXT; | |
#if 0 | |
/*---------------------------Function Prototype-----------------------*/ | |
static int minor_open(struct inode *, struct file *); | |
static int minor_close(struct inode *, struct file *); | |
static ssize_t minor_write(struct file *, const char __user *, size_t, loff_t *); | |
static ssize_t minor_read(struct file *, char __user *, size_t, loff_t *); | |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) | |
static long minor_ioctl(struct file *, unsigned int, unsigned long); | |
#else | |
static int minor_ioctl(struct inode*, struct file *, unsigned int, unsigned long); | |
#endif | |
/*--------------------------------------------------------------------*/ | |
#endif | |
#endif /* _HELLO_H_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment