分类: LINUX
2008-09-25 17:03:26
首先你要有一份2.6内核的源码,然后编译并运行它。
在已运行了你编译的内核的系统里编译此驱动程序: $> make
编译成功后,以root身份运行加载脚本: $> ./chardev_load.sh
一切完成后,就可以测试你的驱动程序了。
此驱动程序的输出全部写到了 /var/log/debug 文件中,你可以如下查看:
$> tail -f /var/log/debug
我用的是 Linux debian
/*
*
chardev.c kf701
* /dev/kf701_chardev0 /proc/devices
*/
/* init.h中定义了宏module_init(x)与module_exit(x)
* 'kernel.h'
contains some often-used function prototypes etc
* module.h
:Dynamic loading of modules into the kernel.包括try_module_get, module_put
*fs.h包括struct
file_operations的定义
*uaccess.h包括User
space memory access functions,如put_user,get user
*cdev.h包括字符设备结构struct cdev定义
*/
#include
#include
#include
#include
#include
#include
static
int chardev_open(struct inode *,
struct file *);
static
int chardev_release(struct inode *,
struct file *);
static
ssize_t chardev_read(struct file *, char
__user *, size_t, loff_t *);
static
ssize_t chardev_write(struct file *,
const char __user *, size_t, loff_t *);
#define
SUCCESS 0
#define
DEVICE "kf701_chardev"
int
major = 233;
int
minor = 0;
struct
file_operations fops = {
.owner = THIS_MODULE, //THIS_MODULE包含在module.h当中,是一个宏
.read = chardev_read,
.write = chardev_write,
.open = chardev_open,
.release = chardev_release,
};
struct
my_dev{
uint8_t *buf;
uint32_t size;
uint32_t index;
struct semaphore sem;
struct cdev cdev;
}
kf701_dev;
/*
__init和__exit标记函数,__initdata和__exitdata标记数据。此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数据)段中。标记为初始化的函数表明公在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核或内核不允许卸载模块。被此标记的函数将被简单地丢弃。
*/
static
int __init chardev_init(void){
/*1. 分配主设备号,MKDEV是Kdev_t.h中定义的一个宏
在Types.h中有:typedef
__kernel_dev_t dev_t ; dev_t是 unsigned int类型
在Coda.h中有:typedef
u_long dev_t
*/
dev_t devno = MKDEV(
major, minor );
//在fs.h中有语句:extern
int register_chrdev_region(dev_t, unsigned, char *);
//但不知register_chrdev_region具体是在什么地方定义实现的?
int ret = register_chrdev_region( devno, 1,
DEVICE );
if( ret < 0 )
{
/*
kernel.h中:#define KERN_DEBUG "<7>" /* debug-level messages
spinlock.h中定义:asmlinkage
int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2)));
Modpost.h中有下面的声明,modpost.c中有其定义:
void
__attribute__((format(printf, 2, 3)))
buf_printf(struct buffer
*buf, const char *fmt, ...);
*/
printk(KERN_DEBUG
"register major number failed with %d\n", ret);
return ret;
}
printk(KERN_DEBUG "%s:register major
number OK\n",DEVICE);
// 2. 注册设备
cdev_init( &kf701_dev.cdev, &fops
);
kf701_dev.cdev.ops = &fops;
kf701_dev.cdev.owner = THIS_MODULE;
ret = cdev_add( &kf701_dev.cdev, devno,
1 );
if( ret < 0 )
{
printk(KERN_DEBUG "register device
failed with %d\n", ret);
return ret;
}
printk(KERN_DEBUG "%s:register device
OK\n",DEVICE);
// 3. 分配本驱动要使用的内存
kf701_dev.index = 0;
kf701_dev.size = 1024;
kf701_dev.buf = kmalloc( kf701_dev.size,
GFP_KERNEL );
if( NULL == kf701_dev.buf )
{
printk(KERN_DEBUG "kmalloc
failed\n");
return -ENOMEM;
}
printk(KERN_DEBUG "%s:kmalloc buffer
OK\n",DEVICE);
// 初始化信号量
init_MUTEX( &(kf701_dev.sem) );
printk(KERN_DEBUG "%s:init semaphore
OK\n",DEVICE);
return SUCCESS;
}
static
void __exit chardev_exit(void)
{
dev_t devno = MKDEV( major, minor );
cdev_del( &kf701_dev.cdev );
unregister_chrdev_region(
devno, 1 );
printk(KERN_DEBUG "%s:unregister
device OK\n",DEVICE);
}
/*在kernel.h中定义: #define
container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member )
*__mptr = (ptr); \
(type *)( (char *)__mptr -
offsetof(type,member) );})
container_of是给定结构体某个成员的地址反推其结构体地址。ptr是一个member的地址
#define list_entry(ptr,
type, member) \
container_of(ptr, type, member)
container_of是个宏,定义如下:
#define
container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member
) *__mptr = (ptr); \ // typeof是gcc扩展,用于 得知数据类型的,这句话作用大概是看ptr是否是结构体里成
//员变量member的类型,不是编译时将报错,类型检测的
(type *)( (char *)__mptr -
offsetof(type,member) );})
offsetof是计算成员member在结构体的type里面的偏移数,用__mptr的地址减去偏移地址就可以得到type结构体的开始地址
*/
int
chardev_open(struct inode *inode, struct file *file)
{
struct my_dev *dev = container_of(inode->i_cdev,
struct my_dev, cdev);
file->private_data = dev;
printk(KERN_DEBUG "%s:open
OK\n",DEVICE);
return SUCCESS;
}
int
chardev_release(struct inode *inode, struct file *file)
{
printk(KERN_DEBUG "%s:release
OK\n",DEVICE);
return SUCCESS;
}
ssize_t
chardev_read(struct file *file, char __user *buf, size_t count,loff_t *offset)
{
struct my_dev *dev = file->private_data;
ssize_t retval = 0;
if( down_interruptible(&dev->sem) )
return -ERESTARTSYS;
printk(KERN_DEBUG "%s:read down sem OK\n",DEVICE);
if( 0 == dev->index )
goto out;
if( *offset >= dev->index )
goto out;
if( (*offset+count) > dev->index )
count = dev->index - *offset;
if( copy_to_user(buf, dev->buf + *offset,
count) )
{
retval = -EFAULT;
goto out;
}
*offset += count;
retval = count;
out:
up(&dev->sem);
printk(KERN_DEBUG "%s:read up sem
OK\n",DEVICE);
return retval;
}
//
omit offset argument for write
ssize_t chardev_write(struct file *file,
const char __user *buf, size_t count, loff_t *offset)
{
struct my_dev *dev =
file->private_data;
ssize_t retval = -ENOMEM;
if( down_interruptible(&dev->sem) )
return -ERESTARTSYS;
printk(KERN_DEBUG "%s:write down sem
OK\n",DEVICE);
if( count > dev->size )
goto out;
if( copy_from_user(dev->buf, buf, count)
)
{
retval = -EFAULT;
goto out;
}
dev->index = count;
retval = count;
out:
up(&dev->sem);
printk(KERN_DEBUG "%s:write up sem
OK\n",DEVICE);
return retval;
}
module_init(chardev_init);
module_exit(chardev_exit);
MODULE_AUTHOR("kf701.ye at
gmail.com");
MODULE_DESCRIPTION("Study for
kf701");
MODULE_SUPPORTED_DEVICE(DEVICE);
MODULE_LICENSE("GPL");
下面是Makefile内容:
###
# study
# kf_701
#
###
#obj-m += hello.o
obj-m += chardev.o
all:
make -C
/lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make
-C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
加载脚本代码如下:
1 #!/bin/sh
2
3 module="chardev"
4 device="kf701_chardev"
5 mode=666
6
7 echo "insmod now"
8 /sbin/insmod ./$module.ko || exit 1
9
10 rm -f /dev/${device}0
11
12 major=$(awk "{ if(\$2 ==
\"${device}\") print \$1;}" /proc/devices)
13
14 echo "mknod now"
15 mknod /dev/${device}
16
17 group="staff"
18 grep -q '^staff:' /etc/group || group="wheel"
19
20 echo "chgrp now"
21 chgrp $group /dev/${device}0
22 chmod $mode /dev/${device}0
23
下面是读测试方法:
$> cat /dev/kf701_chardev0
写测试如下命令:
$> echo "only for test" > /dev/kf701_chardev0