Is
there any different between kernel 2.4 and kernel 2.6 for writing the kernel
module?
Yes, there are differents. In kernel 2.4 we use
register_chrdev() to allocate and register the file operations structure. But
this method is history now. Below are the type and functions introduced in
kernel 2.6
dev_t, register_chrdev_region, alloc_chrdev_region.
Why
we don’t use register_chrdev()?
register_chrdev() still works in
kernel 2.6, but the problem in kernel 2.4 was, we have limited major number
allocation. We had only 8bit for both major and minor number. So total we could
have only 255 major or minor numbers. Where as in new methods, we have 32 bit
for major and minor numbers.(12 bits for major, 20 bits for minor).
What is the different between
register_chrdev_region and alloc_chrdev_region?
register_chrdev() basically allocates major number and registers the file
operation structure. But register_chrdev_region and alloc_chrdev_region only
allocates major number. (register_chrdev_region allocates static major number. Alloc_chrdev_region
allocates dynamic major number).
What is MAJOR and MINOR numbers?
In unix everything is file, the kernel
device is also consider as file aka special file. These special files are
located in /dev.
You can do open, read, write, these
devices, by doing this you can really access the devices.
Ok,
Now consider you have five devices say simple timer, each timer has one register,
by reading that you can see the different time zone.
We
need only one driver, but five devices./dev/time1, /dev/time2, so on. So when
you open /dev/time1 to read the india time. Your kernel module open should
call, because there must be many kernel module running in our kernel, and each
has open call.
In
our kernel module we register our fops structure with our own major number. And
by doing mknod /dev/time1 c 249 1 we map the node to major number.
249
1- /dev/time1 - our fops
1 is minor number, because we five
devices so minor would be 1, 2, 3, 4, 5.
Main purpose of major number is, we can have many devices with
the same major number.
Simple driver with static major no allocation.
#include <linux/module.h>
#include <linux/kernel.h> //for printk
#include <linux/init.h> //for __init __exit
#include <linux/fs.h> //for file_operations
dev_t dev;
static int __init mymodule_init(void)
{
int ret_val;
dev = MKDEV(248, 0);
ret_val = register_chrdev_region(dev, 1, "mydev");
if(ret_val < 0){
printk("\n Error:
register_chrdev_region \n");
return -1;
}
printk("\n Major No: %d Minor_no : %d \n", MAJOR(dev),
MINOR(dev));
return 0;
}
static void __exit mymodule_exit(void)
{
unregister_chrdev_region(dev, 1);
printk ("Unloading my module.\n");
return;
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");
output:
make
insmod test_module.ko
dmesg | tail -20
Major No: 248 minor_no : 0
What is dev_t?
dev_t is the data type introduced in
kerne 2.6. its 32 bit, first 12 bits holds the major number and remaining 20
bits holds the minor number.
Example:
dev_t
dev;
dev
= 0x 0F800001
MAJOR(dev)
= 0x0F800001 >> 20 = 0x0F8 = 248
MINOR(dev)
= 0x0F800001 & 0x000FFFFF = 0x1 = 1
What is the maximum major number?
dev_t can hold 12 bits for major
number so we can have 4095 major number, if we give more than that, then kernel
will allocate random number.
What are the parameter for register_chrdev_region()?
dev_t, number of consecutive major
numbers, dev name.
Can I give
any number to for major number allocation, I mean less than 4095?
No, you
can see /proc/devices to know the major number which was already allocated, so
you should not give the same number which was already allocated. You can choose
any number which was not allocated.
How do I cross
compile this module for powerpc?
Cross
compile is bit different, then user space program. We need to have changes in
makefile
Makefile:
obj-m :=
test_module.o
all:
#make -C /lib/modules/$(shell uname
-r)/build M=$(PWD) modules
make -C /vobs/linux_2_6_34/ M=$(PWD)
modules
clean:
#make -C /lib/modules/$(shell uname
-r)/build M=$(PWD) clean
make -C /vobs /linux_2_6_34/ M=$(PWD)
clean
the lines
commented out is for x86. Basically we need to give a kernel path which was
compiled for powerpc.
Compiling
also bit different, refer below.
make
CROSS_COMPILE=/vobs/toolchains/ppc/gcc-3.4.6-glibc-2.3.6/powerpc-linux/bin/powerpc-linux-
ARCH=powerpc
How can I allocate
the major number dynamically?
You can
allocate the major number and minor number dynamically during the execution. Refer
below code.
#include <linux/module.h>
#include <linux/kernel.h> //for printk
#include <linux/init.h> //for __init __exit
#include <linux/fs.h> //for file_operations
dev_t dev;
static int __init
mymodule_init(void)
{
int ret_val;
ret_val = alloc_chrdev_region(&dev,
0, 1, "mydev_dynamic");
if(ret_val < 0){
printk("\n Error:
alloc__chrdev_region \n");
return -1;
}
printk("\n Major No: %d Minor_no : %d
\n", MAJOR(dev), MINOR(dev));
return 0;
}
static void __exit
mymodule_exit(void)
{
unregister_chrdev_region(dev, 1);
printk ("Unloading my
module.\n");
return;
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");
output
make
insmod test_module.ko
dmesg | tail -1
Major No: 226 Minor_no : 0
alloc_chrdev_region() allocates dynamic major number. It accepts dev_t,
starting minor number, no.of minors, device name.
Does alloc_chrdev_region()
or register_chrdev_region() register the file operation structure?
No,
these two function just allocates major number and registers the device, whereas
the old register_chrdev() allocates major number and registers the file
operations structure and the device.
What is
file operations structure?
File
operations structure is available in linux/fs.h, this structure has the field
for open, read, write callbacks ponters. By registering this structure in your
kernel, you can access the devices from the user space program.
How to
register file operations structure with the kernel?
Below is
code.
#include linux/module.h
#include linux/kernel.h //for printk
#include linux/init.h
//for __init __exit
#include linux/fs.h //for file_operations
#include linux/cdev.h //for cdev struct
dev_t dev;
struct cdev
*my_cdev;
static int
my_open(struct inode *inode, struct file
*file)
{
printk("\n my_open \n");
return 0;
}
static
struct file_operations fops = {
.open = my_open
};
static int
__init mymodule_init(void)
{
int ret_val;
ret_val = alloc_chrdev_region(&dev,
0, 1, "mydev_dynamic");
if(ret_val < 0){
printk("\n Error:
alloc__chrdev_region \n");
return -1;
}
printk("\n Major No: %d Minor_no :
%d \n", MAJOR(dev), MINOR(dev));
my_cdev = cdev_alloc();
my_cdev->owner = THIS_MODULE;
my_cdev->ops = &fops;
cdev_add(my_cdev, dev, 1);
return 0;
}
static void
__exit mymodule_exit(void)
{
unregister_chrdev_region(dev, 1);
cdev_del(my_cdev);
printk ("Unloading my
module.\n");
return;
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");
What is cdev?
cdev is
newly introduced in kernel 2.6. This cdev structure is an internal
representation of a char device. It has a member field as struct file
operations pointer. You have to create and populate your file operations
structure and assign the address of that structure to this member.
# include
struct cdev
{
struct kobject kobj;
struct module* owner;
const struct file_operations* ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
What is cdev_init
and cdev_alloc?
cdev_alloc() allocates cdev object and return to you. Then you need to
give fops pointer
cdev_init() will be used, if you manually allocate using kmalloc, in this
way you don’t need to assign fops manualy.
Example:
1)
struct cdev *cdev;
cdev = cdev_alloc();
cdev->fops = &fops
2)
struct foo {
struc cdev cdev;
int flag;
};
foo f ;
f= kmalloc(100);
cdev_init(f.cdev, &fops);
Here is the complete kernel module and
the user application.
#include <linux/module.h>
#include <linux/kernel.h> //for printk
#include <linux/init.h> //for __init __exit
#include <linux/fs.h> //for file_operations
#include <linux/cdev.h> //for cdev struct
#include <linux/types.h> //for ssize_t
#include <linux/slab.h> //for kmalloc
#include <asm/uaccess.h> //for copy_to_user
#define ADD
0x1
#define SUB
0x2
#define MUL
0x3
dev_t dev;
struct cdev
*my_cdev;
char *ptr;
struct foo
{
int a;
int b;
int c;
};
static int
my_open(struct inode *inode, struct file
*file)
{
printk("\n Device Major : %d
\n", imajor(inode));
printk("\n Device Minor : %d
\n", iminor(inode));
ptr = kmalloc(10, GFP_KERNEL);
return 0;
}
static int
my_release(struct inode *inode, struct file *file)
{
kfree(ptr);
printk("\n Device Released and
freed memory \n");
return 0;
}
static
ssize_t my_read(struct file *filp,char *buffer,
size_t
length,loff_t *offset)
{
copy_to_user(buffer, ptr, length);
return 0;
}
static
ssize_t my_write(struct file *filp, const char *buffer,
size_t length,
loff_t *offset)
{
printk("\n Receiced String :%s
\n", buffer);
copy_from_user(ptr, buffer, length);
return length;
}
static long
my_ioctl(struct file *filp, unsigned int cmd,
unsigned long args)
{
struct foo f;
switch(cmd){
case ADD:
copy_from_user(&f,
(struct foo *)args, sizeof(struct foo));
f.c = f.a + f.b;
copy_to_user((struct
foo *)args, &f, sizeof(struct foo));
break;
case SUB:
copy_from_user(&f,
(struct foo *)args, sizeof(struct foo));
f.c = f.a - f.b;
copy_to_user((struct
foo *)args, &f, sizeof(struct foo));
break;
case MUL:
copy_from_user(&f,
(struct foo *)args, sizeof(struct foo));
f.c = f.a * f.b;
copy_to_user((struct
foo *)args, &f, sizeof(struct foo));
break;
default:
copy_from_user(&f,
(struct foo *)args, sizeof(struct foo));
f.c = 0;
copy_to_user((struct
foo *)args, &f, sizeof(struct foo));
break;
}
return 0;
}
static
struct file_operations fops = {
.open = my_open,
.read = my_read,
.write = my_write,
.release = my_release,
.unlocked_ioctl = my_ioctl
};
static int
__init mymodule_init(void)
{
int ret_val;
ret_val = alloc_chrdev_region(&dev,
0, 1, "mydev_dynamic");
if(ret_val < 0){
printk("\n Error:
alloc__chrdev_region \n");
return -1;
}
printk("\n Major No: %d Minor_no :
%d \n", MAJOR(dev), MINOR(dev));
my_cdev = cdev_alloc();
my_cdev->owner = THIS_MODULE;
my_cdev->ops = &fops;
cdev_add(my_cdev, dev, 1);
return 0;
}
static void
__exit mymodule_exit(void)
{
unregister_chrdev_region(dev, 1);
cdev_del(my_cdev);
printk ("Unloading my
module.\n");
return;
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.Kalimuthu");
User
Application:
#include<stdio.h>
#include<fcntl.h>
#define ADD
0x1
#define SUB
0x2
#define MUL
0x3
#define
UNKNOWN 0x4
struct foo{
int a;
int b;
int c;
};
int main()
{
struct foo
f;
int fd;
char
buff[15];
fd =
open("/dev/mydev_dynamic", O_RDWR);
write(fd,
"Hello world", 12);
read(fd,
buff, 12);
printf("%s",
buff);
f.a = 15;
f.b = 10;
ioctl(fd,
ADD, &f);
printf("\n
Add : %d \n", f.c);
ioctl(fd,
SUB, &f);
printf("\n
Sub : %d \n", f.c);
ioctl(fd,
MUL, &f);
printf("\n
mul : %d \n", f.c);
ioctl(fd,
UNKNOWN, &f);
printf("\n
Unknown : %d \n", f.c);
close(fd);
return 0;
}
Output:
./a.out
Hello world
Add : 25
Sub : 5
mul : 150
Unknown : 0
dmesg |
tail -10
Major No: 226 Minor_no : 0
Device Major : 226
Device Minor : 0
Receiced String :Hello world
Device Released and freed memory
How does
the open call in user context move to kernel context?
Basically,
the open call triggers the soft interrupt we say that 0x80, this interrupt is
for system call(). This is initialized in the interrupt vector table during set_system_gate
(0x80, &system_call. This system call interrupt forces the context switch.
Why
copy_to_user or copy_from_user? Can we use memcpy?
Hmm, I
guess the memcpy also works fine, but the main reason for copy_to_user or
copy_from_user is mainly for protection. We don’t want a user space program to
make a panic in kernel. copy_to_user or copy_from_user checks the memory before
read or write.