Skip to content

Instantly share code, notes, and snippets.

@jagannath-sahoo
Created May 11, 2019 13:47
Show Gist options
  • Save jagannath-sahoo/457d70249cd7d1014bc7f74deecf79b7 to your computer and use it in GitHub Desktop.
Save jagannath-sahoo/457d70249cd7d1014bc7f74deecf79b7 to your computer and use it in GitHub Desktop.
Jerin
//include appropriate headers
//
//
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/proc_fs.h>
#include<linux/slab.h>
#include<linux/seq_file.h>
#include<linux/list.h>
//refer to ch11 of LDD/3 for the data-structures and related macros,
//used in this example !!!
//refer to chapter 3 of ULK/3
//
//intrestingly, similar setup is used for pds, in process management !!!
/* Private Data structure */
struct _mydrv_struct {
/* ... */
struct list_head list; /* Link to the next node */ //this is fixed for any type of struct
char info[13];
char info1[10]; /* Info to pass via the procfs file */ //these elements differ
//based on your module/driver
/* ... */
};
//refer to ch11 of LDD/3
static LIST_HEAD(mydrv_list); /* List Head */
static struct proc_dir_entry *entry = NULL ;
/* start() method */
//
//whatever index is requested, we send back the pointer of that object
//
//*pos gives the index of the requested element in your list
//typically, it is 0, when navigation is started by a read()(first read)
//during run-time,*pos tells us where are we in the list access -
//specific object's index
// in the list -
// the specific node in the list
//param1 is the object created for this active file in the
//sequence file layer - it may be used in our operations ' code
//
//param2 is pointer to the offset field in the open file object of
//this active file - however, in this context it does not represent
//file logical byte no - instead, it represents index of the system
//object , in the system's data structure that is navigated, when
//this procfs file is accessed using an active file !!! in fact, when
//an active file of this procfs file is accessed/navigated/read,
//data from system objects maintained in the corresponding system
//data structure is retrieved and passed to user-space !!!
//
//system passes a *pos (offset/index of an object), as per the
//current state of navigation/access !!! to start with the value will
//be 0 - as one or more objects are navigated/accessed, this value will
//be incremented - we must interpret this value, validate the value and
//take appropriate actions, as per rules !!!
//
//as per rules, we must do the following, in xxx_seq_start() :
//- using the *pos(index value), navigate related system data-structure
// and extract ptr to
// corresponding object
//- return ptr to the extracted object, if there is such object for
// the provided index !!!
//- otherwise return NULL
//- all we need to code is the above rules and implement the same !!!
static void *
mydrv_seq_start(struct seq_file *seq, loff_t *pos)
{
struct _mydrv_struct *p;
loff_t off = 0;
/* The iterator at the requested offset */
dump_stack();
//system macro used to navigate our private ds !!!
list_for_each_entry(p, &mydrv_list, ) {
if (*pos == off++) {
printk("in start : success %lld\n",*pos);
return p; //if my position is reached
// return the ptr
}
}
printk("in seq_start : over\n");
return NULL;
}
/* next() method */
//whenever seq_read() needs more data from more objects, it will invoke
//xxx_seq_next ???
//
//advance to the next position and return the pointer to that object
//
//as per the rules, we must do the following in this method :
//
//using ptr(known as iterator by the kernel document)
//to the current object, get the ptr to the next object
//in the system data-structure !!!
//
//in addition, increment the offset (index) using *pos !!!
//
//eventually, return ptr to the next object in the data structure
//however, if we have navigated the list completely, return NULL !!!
//
static void *
mydrv_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
//here, our real objective is to increment the *pos
//and return ptr to the next object based on what is
//passed to us by system
//we still must check if the next element is the first element
//and if so , we have reached end of list and we must
//return NULL
dump_stack();
/* 'v' is a pointer to the iterator returned by start() or
by the previous invocation of next() */
struct list_head *n = ((struct _mydrv_struct *)v)->list.next;
++*pos; /* Advance position */
/* Return the next iterator, which is the next node in the list */
printk("in seq_next :%lld\n",*pos);
return(n != &mydrv_list) ?
list_entry(n, struct _mydrv_struct, list) : NULL;
}
/* show() method */
//invariably, show() method is invoked just after start() method or
//after next() method !!!
//in addition, show() method is passed ptr to the object that
//was returned by start()/next() to seq_read() !!!
//
//in addition, we must copy data from the object pointed to by
//v into the kernel buffer maintained in struct seq_file object pointed to
//by seq - there are helper functions to do this - do not do by
//hand - if you do so, it will corrupt any existing data in the
//buffer !!!
//these actions are taken as per rules - if you need more information,
//refer to document that contains the rules !!!
static int
mydrv_seq_show(struct seq_file *seq, void *v)
{
int ret;
struct _mydrv_struct *p =v;
dump_stack();
/* Interpret the iterator, 'v' */
printk("in seq_show \n");
//what ever information that you wish to generate on the fly, it
//must be generated and passed as below
//using seq_printf() we can pass info. to user space, via
//sequence file layer's kernel buffer !!!first data is copied
//into kernel buffer from the object of a subsystem or component !!
//
//kernel buffer is used with seq_printf(provided by sequence file layer)
//we are copying data from a current object of navigation into
//kernel buffer of *seq !!!
//ret = seq_printf(seq,"%s\n",p->info);
seq_printf(seq,"%s\n",p->info);
//printk(KERN_INFO "the return value of seq_printf is %d\n", ret);
return 0;
}
//typically invoked by seq_read(), when required data is copied or
//no more data is present for copying !!!
//
//this is suppposed to be used to free any resources allocated
//during start method !!!
/* stop() method */
static void
mydrv_seq_stop(struct seq_file *seq, void *v)
{
//stop() is normally used for freeing any resource allocated during
//start() !!!
/* No cleanup needed in this example */
dump_stack();
printk("in seq_stop:\n");
}
//if you have understood the rules for coding the below methods,
//you can code and provide a set of methods to the sequence file
//layer, which inturn serves the procfs layer
//
//here, procfs, sequence file layer and our module are connected-
//these methods are provided by our module to copy data from
//appropriate objects of system data structure to sequence file
//layer's buffer maintained in seq_file { } object for our
//active file !!!
//
//from the sequence file layer, data is copied to users-space
//buffer !!!
//
//whenever, our procfs file is accessed, associated data structure
//is navigated and information extracted from objects maintained
//in the data structure are copied to users-space buffer - however,
//the whole frame-work works, if you have written these methods as per
//rules and validated their working !!!
/* Define iterator operations */
static struct seq_operations mydrv_seq_ops = {
.start = mydrv_seq_start,
.next = mydrv_seq_next,
.stop = mydrv_seq_stop,
.show = mydrv_seq_show,
};
//this method is invoked, when active file is created, when
//our pseudofs file is opened using open(" ", ) system call API !!!
static int
mydrv_seq_open(struct inode *inode, struct file *file)
{
//printk writes the message in the kernel's log-buffer
//using dmesg, you can read from the kernel's log-buffer
printk("we are in mydrv_seq_open\n"); //1
/* Register the operations */
dump_stack(); //used for diagnostic messages
//with the help of dump_stack(), we understand that our
//open method is invoked by procfs layer, which is connected
//to logical file system manager
//further, our open method also registers another table
//of methods/operations using seq_open() !!! this system API
//enables us to register another set of operations with
//sequence file layer of the system !!! this must be done
//as per the rules of pseudo files of procfs !!!
//as per the rules and what is available from
//<ksrc>/include/linux/seq_file.h & <ksrc>/fs/seq_file.c(is the
//sequence file layer)
//
//following is done during seq_open()
//
//internally, struct seq_file {} object is created and
//filled with another operations table ptr and in addition,
//the new struct seq_file {} object is linked to active file
//object of the current active file(via open file object
//of this active file - private_data field of the open file object
//of this active file is used to store the ptr to
//struct seq_file { } object instance ) !!!this is all done with
//respect to sequence file layer, not procfs layer - however,
//procfs and sequence file layer are connected !!!
//we are registering our table with sequence file layer -
//as part of it, certain objects are connected and may be
//used during run-time !!!
return seq_open(file, &mydrv_seq_ops);
}
//for the current context, a file_operations object is created
//and initialized as per rules of the system - these rules are
//documented under <ksrc>/Documentation/filesystems/seq_file.txt
//
//every kernel module object file creates a struct module{} in the
//system space, when loaded - this struct module{} is used to manage
//the loaded kernel module object file, in the system !!!
static struct file_operations mydrv_proc_fops = {
.owner = THIS_MODULE, //this macro will provide the ptr to our module object
.open = mydrv_seq_open, /* User supplied */ //passing addresses of functions
//to function pointers
.read = seq_read, //Built-in helper functions taken from
//fs/seq_file.c
.llseek = seq_lseek, //fs/seq_file.c provides sequence file
//layer - sequence file layer provides
.release = seq_release, //support to procfs layer
};
//assuming this module is compiled and loaded, what
//will be the consequences ?? what are the actions taken
//by the system ??
// a new proc file will be created and visible from the
//procfs to the user-space !!! this procfs pseudo file
//provides interface to our module, in this context -
//when this procfs file is accessed, several call backs
//of the kernel space are invoked, including our modules
//call backs ???
//what next ???
//we need to access the procfs file, using file related system
//call APIs !!! in this context, we may use open(), read(), ioctl(),
//and close() system call APIs
//assuming that we are opening a procfs file, using open -
//fd = open("/proc/readme", O_RDONLY) - what will be the
//actions taken by the system ???
//in this context , the first system call API will be
//open() system call API, which will be the first
//system call API invoked, by an application code or
//an utility ?? what happens, when this open system call API is
//invoked - refer to the call backs and descriptions above ??
//what next ??
//let us understand the set-up of a procfs file, based on the
//code of the init method
static int __init
mydrv_init(void)
{
/* ... */
int i;
struct _mydrv_struct *mydrv_new;
dump_stack();
/* ... */
/* Create /proc/readme */
//this will create a new proc file with following attributes !!!
//name
//permissions
//ptr to the parent directory's object (proc_dir_entry{})
//last parameter may be NULL, if our file is to be created
//under /proc directory as the parent directory - otherwise,
//it must not be NULL !!
//return value provides pointer to struct proc_dir_entry{} of the
//new proc file that has been created !! a newly created procfile
//object is added to a list under the object of the corresponding
//parent directory !!!
//refer to include/linux/proc_fs.h
//refer to fs/proc/generic.c
//entry1 = proc_mkdir("proc_test", NULL);//a file is created
//entry = create_proc_entry("readme", S_IRUSR, entry1);//a file is created
//We cannot use create_proc_entry(), int the newer kernel versions - so, we will be
//using proc_create()
//check <ksrcdir>/include/linux/proc_fs.h
//struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct
//proc_dir_entry *parent, const struct file_operations *proc_fops);
//entry = create_proc_entry("readme", S_IRUSR, NULL);//a file is created
entry = proc_create("readme", S_IRUSR, NULL, &mydrv_proc_fops);
//The following below line are deprecated way of usage of function
/* Attach it to readme_proc() */
//check for error - NULL
/*if (entry) {
/* Replace the assignment to entry->read_proc in proc_example1.c
with a more fundamental assignment to entry->proc_fops. So
instead of doing "entry->read_proc = readme_proc;", do the
following: */
//we are replacing an entry in the proc_dir_entry to our convenience
//this is a form of registration, where a table of our methods
//are added to the system via a system object's field - in this case,
//we are using proc_fops field of proc_dir_entry {} - this is done
//as per the rules !!! by doing this, we are providing custom
//operations(call backs) to our procfs file - whenever,
//our active file(??) of our procfs file is
//accessed, these custom methods will be accessed and accordingly,
//data will be processed/retrieved !!!
//for a regular file of a regular filesystem, we need not
//provide custom operations and system's apppropriate
//file system manager will provide the operations table 1!
/*
entry->proc_fops = &mydrv_proc_fops; //done as per rules of the frame-works of
//procfs and
} //sequence file layer
else
{
return -EINVAL; //init method has failed !!!
}
*/
/* Handcraft mydrv_list for testing purpose.
In the real world, device driver logic or kernel's sub-systems
maintain the list of objects and populates the fields */
//we are creating our own dummy(private) objects and
//maintaining them in a
//system defined dummy data-structure - all rules/apis are provided
//by the system !!! for a basic demo and illustration !!!
//refer to chapter 11 of LDD/3 for details of objects and
//data structure !!!
//kmalloc() is a system API that allocates physical memory
//and returns appropriate starting virtual/logical address of
//physical memory - there are several rules - for the time being,
//let us use the default calling conventions !!!
//you need not create objects and maintain a list in assignment 2
//you will be using system objects(pd objects) and
//system data structures in assignment 2
//in assignment 5, you will be accessing private objects of
//your devices and drivers !!!
for (i=0;i<100;i++) {
mydrv_new = kmalloc(sizeof(struct _mydrv_struct), GFP_KERNEL);
//check errors
sprintf(mydrv_new->info, "Node No: %d\n", i);
list_add_tail(&mydrv_new->list, &mydrv_list);
}
printk("we are in init function of the module\n"); //2
return 0;
}
static void mydrv_exit(void)
{
dump_stack();
//incomplete
struct _mydrv_struct *p,*n;
//list_for_each_entry_safe(p,n, &mydrv_list, list)
// kfree(p);
list_for_each_entry_safe(p,n, &mydrv_list, list){
kfree(p);
//additional steps are added
}
remove_proc_entry("readme", NULL);
//remove_proc_entry("readme", entry1);
//remove_proc_entry("proc_test", NULL);
printk("mydrv_exit just executed\n"); //3
}
module_init(mydrv_init);
module_exit(mydrv_exit);
//add other macros as needed
MODULE_LICENSE("GPL v2");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment