Skip to content

Instantly share code, notes, and snippets.

@gauthamkantharaju
Created September 20, 2013 03:44
Show Gist options
  • Save gauthamkantharaju/6633042 to your computer and use it in GitHub Desktop.
Save gauthamkantharaju/6633042 to your computer and use it in GitHub Desktop.
Simple character device driver with debugfs entry
#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;
}
/**
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 <gautham.kantharaju@gmail.com>");
#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