Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Pisanje kernel modula - Linux Kernel Module Programming
#1
Brick 
Ovih dana sam experimentisao malo sa pisanjem Linux kernel modula (device driver) i dostavljam ovde jedan prost primer kao osnovu - skeleton.

Ovde pokazujem jedan primer za "Character Device" (suprotno od "Block Device") driver, parce dinamickog programa koji se ucitava u kernel i koji operise sa tkz seriskim tipovima uredjaja, npr raznim komunikacioni portovi poput RS232, I2C, USB i slicno.
Primetice te da nije uradjena fizicka komunikacija vec je ceo drajver napravljen kao "fake", tj moze u njega da se upisuje i posle da se iscitavaju podaci, kao da imamo prikacenu neku serisku memoriju od 100 bajtova ("virtual_device").

Razvojno okruzenje je Linux Debian 8 sa potrebnim paketima (gcc, make, linux-headers …)

hello.c C++ source code (zanemarite PHP, iskorisceno za forum samo za highligh sintakse)

PHP Code:
/**********************************************************
***********************************************************
hello.c
***********************************************************
***********************************************************/

#include <linux/init.h> //MUST
#include <linux/module.h> //MUST
#include <linux/moduleparam.h> //to pass params
#include <linux/fs.h> //File system operations, read/write, open/close device drivers
#include <linux/cdev.h> //characher device driver
#include <linux/semaphore.h> //used to access semaphores; sunchronization behaviors
#include <asm/uaccess.h> //copy_to_user; copy_from_user

//other variables
struct cdev *mcdev//my charachter device
int major_number//will store our major number - extracted from dev_t using macro - mknod /directory/files c major minor
int ret//used to hold return values of functions; this is because kernel stack is very small so declare variables all over the pass in our module functions eats up the stack very fast
dev_t dev_num// hold major naumber that kernel give us; name --> apears in /proc/devices

#define DEVICE_NAME "mikidev"

//structure for device
struct fake_device {
    
char data[100];
    
struct semaphore sem;
virtual_device;


//***********************************************************/
//***********************************************************/
//***********************************************************/

//pass the params (not used now)
int my_param 0;
module_param(my_paramintS_IRUSR S_IWUSR);


//***********************************************************/
//********************** device operation  ******************/
//***********************************************************/

//called on device_file open
//inode reference to the file on disk
//and contains information about the file
//struct files is represents an abstract open file
int device_open (struct inode *inodestruct file *filp) {

    
//only allow one process to open this device by using semaphore as mutual exclusive lock - mutex
    
if (down_interruptible(&virtual_device.sem) != 0) {
        
printk(KERN_ALERT "%s: could not lock device during open"DEVICE_NAME);
        return -
1;
    }

    
printk(KERN_INFO "mikidev: opened device");
    return 
0;

}

ssize_t device_read (struct filefilpcharbufStoreDatasize_t bufCountloff_tcurOffset) {
    
//take the kernel space (device) to user space (process)
    //copy_to_user (destiantion, source, sizeToTransfer)
    
printk(KERN_INFO "%s: Reading from device"DEVICE_NAME);
    
ret copy_to_user(bufStoreDatavirtual_device.databufCount);
    return 
ret;
}

ssize_t device_write (struct filefilp, const charbufSourceDatasize_t bufCountloff_tcurOffset) {
    
//send data from user to kernel
    //copy_from_user (destiantion, source, sizeToTransfer)
    
printk(KERN_INFO "%s: Writng to device"DEVICE_NAME);
    
ret copy_from_user(virtual_device.databufSourceDatabufCount);
    return 
ret;
}

int device_close(struct inode *inodestruct file *filp) {
    
//by calling up, whic is opposite of down for semaphore, we realease the mutex we obtained ad device open
    //this has the effect of allowing other process to use the device now
    
up(&virtual_device.sem);
    
printk(KERN_INFO "%s: closed device"DEVICE_NAME);
    return 
0;
}

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


//***********************************************************/
//********************** module operation *******************/
//***********************************************************/
static int driver_init (void) {
    
    
//register our device with the system: a two step process
    //step 1 use dynamic alloacation to assign our device a major number 
    // -- alloc_chrdev_region (dev_t*, uint fminor, uint count, char* name)
    
ret alloc_chrdev_region (&dev_num0,1DEVICE_NAME);
    if (
ret 0) {//alert time kernel functions return negatives, there is an error
        
printk(KERN_ALERT "%s: faild to allocate a major number"DEVICE_NAME);
        return 
ret//propagate error
    
}
    
    
major_number MAJOR(dev_num); //extract the major number and store into our variables (MACRO)
    
printk(KERN_INFO "%s: major number is %d"DEVICE_NAMEmajor_number);
    
printk(KERN_INFO "\tuse \"mknod /dev/%s c %d 0\" for device file"DEVICE_NAMEmajor_number); //dmesg
    
    
mcdev cdev_alloc(); //create our cdev structure, initialize our cdev
    
mcdev->ops = &fops//struct file_operations
    
mcdev->owner THIS_MODULE;

    
//now 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(mcdevdev_num1);
    if (
ret 0) { //always check errors
        
printk(KERN_ALERT "%s: unable to add cdev to kernel"DEVICE_NAME);
        return 
ret;
    }
    
    
//initialise our semaphore
    
sema_init (&virtual_device.sem1); //initial values of one
    
    
return 0;
}

static 
void driver_exit (void) {

    
//unregister evrything in reverse order
    
cdev_del(mcdev);
    
    
unregister_chrdev_region(dev_num1);
    
printk(KERN_ALERT "%s: unloaded module"DEVICE_NAME);

}

//***********************************************************/
//********************** init *******************************/
//***********************************************************/
//inform the kernel where to start and stop with our module/driver
module_init (driver_init);
module_exit (driver_exit);

MODULE_AUTHOR("A. Markovic");
MODULE_DESCRIPTION("My first kernel module");
MODULE_LICENSE("GPL"); 

Makefile (skripta za utomatsko kompajliranje)

PHP Code:
obj-:= hello.o


KERNEL_DIR 
= /usr/src/linux-headers-$(shell uname -r)

all:
    $(
MAKE) -$(KERNEL_DIRSUBDIRS=$(PWDmodules
    
clean
:
    
rm -rf *.*.ko *.mod.* *.symvers *.order 

U ovom Makefile vidite da se poziva hederi za trenutni OS na kome radite ali ako tu explicitno navedete neku drugu verziju hedera, recimo specificnu za RPi, moze bez problema da se iskompalira i za druge procesore.

Koga ovo bude interesovalo neka slobodno postavi pitanja Smile

Sve ovo je opisano u odlicnom YT tutorialu (8 delova):
https://www.youtube.com/watch?v=-O6GsrmOUgY

Nakon uspesnog kompajliranja (poziva se "make"), treba da dobijete hello.ko fajl koji predstavlja sam driver.
Drajver se ucitava u kernel sa komandom "insmod hello.ko";
Tokom ucitavanja sam drajver ce ispisati nekoliko poruka i te poruke mogu da se vide u syslog (dmesg);
Iskljucivanje / deaktivacija drajvera se radi komandom "rmmod hello".


Attached Files
.c   hello.c (Size: 5,71 KB / Downloads: 1)
Reply


Messages In This Thread
Pisanje kernel modula - Linux Kernel Module Programming - by mikikg - 06-14-2015, 08:53 PM
Novi RPi 3B+ - by ognjan - 03-16-2018, 12:43 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)