Skip to main content
Okay so I have posted a number of Linux kernel programming videos over at my YouTube channe, however I find that I need a place to post a sample code and the make file to build a module.

So here it is. The Makefile


obj-m := solidusmodule.o


KERNEL_DIR = /lib/modules/3.2.0-25-generic/build
PWD := $(shell pwd)
all:
 $(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules

clean:
 rm -rf *.o *.ko *.mod.* *.symvers *.order *~


#include 
#include 
#include       // file_operations structure- which of course allows use to open/close,read/write to device
#include       //this is a char driver; makes cdev available
#include  //used to access semaphores; sychronizatoin behaviors
#include      //copy_to_user;copy_from_user


//(1) Create a structure for our fake device
struct fake_device {
 char data[100];
 struct semaphore sem;
} virtual_device;

//(2) To later register our device we need a cdev object and some other variables
struct cdev *mcdev; //m stands 'my'
int major_number; //will store our major number- extracted from dev_t using macro - mknod /director/file c major minor
int ret;  //will be used to hold return values of functions; this is because the kernel stack is very small
   //so declaring variables all over the pass in our module functions eats up the stack very fast 
dev_t dev_num;  //will hold major number that kernel gives us
   //name--> appears in /proc/devices
#define DEVICE_NAME "solidusdevice"



//(7)called on device_file open
// inode reference to the file on disk
// and contains information about that file
// struct file is represents an abstract open file
int device_open(struct inode *inode, struct file *filp){
 
 //only allow one process to open this device by using a semaphore as mutual exclusive lock- mutex
 if(down_interruptible(&virtual_device.sem) != 0){
  printk(KERN_ALERT "soliduscode: could not lock device during open");
  return -1;
 }
 
 printk(KERN_INFO "soliduscode: opened device");
 return 0;
}
//(8) called when user wants to get information from the device
ssize_t device_read(struct file* filp, char* bufStoreData, size_t bufCount, loff_t* curOffset){
 //take data from kernel space(device) to user space (processs)
 //copy_to_user (destination, source,sizeToTransfer)
 printk(KERN_INFO "soliduscode: Reading from device");
 ret = copy_to_user(bufStoreData,virtual_device.data,bufCount);
 return ret;
}
//(9) called when user wants to send information to the device
ssize_t device_write(struct file* filp, const char* bufSourceData,size_t bufCount, loff_t* curOffset){
 //send data from user to kernel
 //copy_from_user (dest, source, count)

 printk(KERN_INFO "soliduscode: writing to device");
 ret = copy_from_user(virtual_device.data, bufSourceData, bufCount);
 return ret;
}
//(10) called upon user close
int device_close(struct inode *inode, struct file *filp){
 

 //by calling up , which is opposite of down for semaphore, we release the mutex that we obtained at device open
 //this has the effect of allowing other process to use the device now
 up(&virtual_device.sem);
 printk(KERN_INFO "soliduscode: closed device");
 return 0;
}


//HERE
//(6) Tell the kernel which functions to call when user operates on our device file
struct file_operations fops = {
 .owner = THIS_MODULE,  //prevent unloadind of this module when operations are in use
 .open = device_open,  //points to the method to call when opening the device
 .release = device_close, //points to the method to call when closing the device
 .write = device_write,  //points to the method to call when writing to the device
 .read = device_read  //points to the method to call when reading from the device
};


static int driver_entry(void){
 
 //(3) Register our device with the system: a two step process
 //step(1) use dynamic allocation to assign our device
 //    a major number-- alloc_chrdev_region(dev_t*, uint fminor, uint count, char* name)
 ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);
 if(ret < 0) { //at time kernel functions return negatives, there is an error
  printk(KERN_ALERT "soliduscode: failed to allocate a major number");
  return ret; //propagate error
 }
 major_number = MAJOR(dev_num); //extracts the major number and store in our variable (MACRO)
 printk(KERN_INFO "soliduscode: major number is %d",major_number);
 printk(KERN_INFO "\tuse \"mknod /dev/%s c %d 0\" for device file",DEVICE_NAME,major_number); //dmesg
 //step(2)
 mcdev = cdev_alloc(); //create our cdev structure, initialized our cdev
 mcdev->ops = &fops;  //struct file_operations
 mcdev->owner = THIS_MODULE; 
 //now that we created cdev, we have to add it to the kernel 
 //int cdev_add(struct cdev* dev, dev_t num, unsigned int count)
 ret = cdev_add(mcdev, dev_num, 1);
 if(ret < 0) { //always check errors
  printk(KERN_ALERT "soliduscode: unable to add cdev to kernel");
  return ret;
 }
 //(4) Initialize our semaphore
 sema_init(&virtual_device.sem,1); //initial value of one

  return 0; 
}

static void driver_exit(void){
 //(5) unregister everyting in reverse order
 //(a)
 cdev_del(mcdev);

 //(b)
 unregister_chrdev_region(dev_num, 1);
 printk(KERN_ALERT "soliduscode: unloaded module");
}

//Inform the kernel where to start and stop with our module/driver
module_init(driver_entry);
module_exit(driver_exit);




Comments

  1. great work!!!!!!!1
    very helpful for beginners......

    please also post the userapp program to

    ReplyDelete
  2. Thanks for the Linux kernel module tutorials!
    How can I contact you privately?

    ReplyDelete

Post a Comment

Popular posts from this blog

Creating local variables In Assembly

Lets go over how to create local variables inside of a pure assembly source code. Much like always, you will start with a *.asm file that looks like this: source code SECTION .data SECTION .bss SECTION .text global main ;make main available to operating system(os) main: ;create the stack frame push ebp push mov ebp, esp ;destroy the stack frame mov esp, ebp pop ebp ret So, the above is the general layout of an NASM source file.  Our goal here is to create a local variable inside of the main method.  The only way to create a local variable is by using the stack.  Why?  Because we can only declare variable in storage locations and the only available storage locations are: text, bss, and data.  However, text section is only for code, so it is out of the question.  The bss and data sections are appealing, but to declare our "local" variable in these sections will defeat the purpose of these variables being local, t

Introduction to Linux Kernel Programming

The Linux kernel is designed as a mixture of a monolithic binary image and a micro-kernel.  This combination allows for the best of both worlds.  On the monolithic side, all the code for the kernel to work with the user and hardware is already installed and ready for fast access, but the downside is that to add more functionality you need to rebuild the entire kernel.   In a different manner, a micro-kernel is composed of small pieces  of code that can be meshed today and more pieces can be added or removed as needed.  However, the downside to micro-kernel is a slower performance. Adding a module to the Kernel Linux is organized as both monolithic, one huge binary, and micro-kernel, as you can add more functionality to it.  The process of adding more functionality to the kernel can be illustrated by the crude image to the left. The process begins by using the command insmod with the name of the kernel module you want (which usually ends with extension *.ko).  From here, the mod

NASM Programming

Many of you, if you are like me, might be interested in how assembly works.  You will be very surprised that assembly is very very easy, especially after you write a couple of simple programs.  But don't get me wrong, you will be frustrated at first, however that frustration, if you channel it right, will lead to serious life long learning and will give you a deeper appreciation of the beauty of assembly. For more tutorial on assembly and visualization of these information, visit my youtube channel . Okay so lets get started. We will be using Netwide Assembler (NASM) to write our program. The general format of NASM file is this: ;This is a comment SECTION .data ;declare variable here SECTION .bss ;declare actual, dynamic variable SECTION .text ;where your program code/assembly code lives ; Working with Data Section In your .data section, you can declare variables like this: nameOfVariable: db 32 ;this declares a variable names nameOfVariable with byte valu