您的位置:首页 > 其它

[设备驱动] 最简单的内核设备驱动--字符驱动

2013-09-06 15:15 323 查看
[设备驱动] 最简单的内核设备驱动--字符驱动
概要:

x86平台上([b]linux-2.6.34.14;[b]Linux debian 3.2.0-3-686-pae[/b])编写一个256字节的字符驱动程序.在/dev下生成设备节点,供用户访问.[/b]

一.目录结构

[root:simple_cdev] tree

.

├── demo.c

├── demo.h

├── example

│ ├── main.cpp

│ └── Makefile

└── Makefile

1 directory, 5 files

二.demo内核驱动模块源码和编译:

2.1 字符驱动demo.c源码:

[root:simple_cdev] cat demo.c
/// @file demo.c
/// @brief
/// @author Easton Woo
/// 0.01
/// @date 2013-09-02

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include "demo.h"

#include <linux/device.h>

int DEMO_open(struct inode *innode, struct file *filep);
int DEMO_close(struct inode *innode, struct file *filp);
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos);
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos);
loff_t DEMO_seek(struct file *filp, loff_t offset, int where);
long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg);

int DEMO_init_module(void);
void DEMO_cleanup_module(void);

struct DEMO_dev
{
struct cdev cdev;
};

struct file_operations DEMO_fops = {
.owner = THIS_MODULE,
.open = DEMO_open,
.release = DEMO_close,
.read = DEMO_read,
.write = DEMO_write,
.llseek = DEMO_seek,
.unlocked_ioctl = DEMO_ioctl,  //.ioctl 成员变量在我的这个内核版本中已经不能用了。
};

struct DEMO_dev* DEMO_devices;
static struct class* class_demo;
static unsigned char demo_inc = 0;
static u8 demoBuffer[256];
#define DEMO_MAJOR 224
#define DEMO_MINNOR 0

//////////////////////设备操作//////////////////////////////
int DEMO_open(struct inode *innode, struct file *filep)
{
struct DEMO_dev * dev = NULL;

if( demo_inc > 0 )
{
return -ERESTARTSYS;
}
demo_inc++;

dev = container_of(innode->i_cdev,struct DEMO_dev,cdev);
filep->private_data = dev;

printk(KERN_WARNING "DEMO: device is open!\n");
return 0;
}

int DEMO_close(struct inode *innode, struct file *filp)
{
if( demo_inc > 0 )
{
demo_inc--;
}
printk(KERN_WARNING "DEMO: device is close!\n");
return 0;
}

ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos)
{
loff_t pos = *p_pos;
int iReadCount = 0;

if(pos >  256 - 1)
{
return -1;
}

if(256 - pos < count)
{
count = 256 -pos;
}

iReadCount = copy_to_user(buf, demoBuffer+pos,count);

if(iReadCount == 0)
{
printk(KERN_INFO "DEMO: read %d data successful\n",count );
}
else
{
printk(KERN_INFO "DEMO: read %d data fail\n",count );
}

count = iReadCount;
return count;
}

ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos)
{
loff_t pos = *p_pos;
int iWriteCount = 0;

if(pos >  256 - 1)
{
return -1;
}

if(256 - pos < count)
{
count = 256 -pos;
}

iWriteCount = copy_from_user(demoBuffer+pos,buf,count);

if(iWriteCount == 0)
{
printk(KERN_INFO "DEMO: write %d data successful\n",count );
}
else
{
printk(KERN_INFO "DEMO: write %d data fail\n",count );
}

count = iWriteCount;
return 0;
}

loff_t DEMO_seek(struct file *filp, loff_t offset, int where)
{
loff_t pos = filp->f_pos;
switch(where)
{
case 0:
pos = offset;
break;
case 1:
pos += offset;
break;
case 2:
pos = 256-1 - offset;
break;
}

if(pos > 256 -1 || pos < 0)
{
return -1;
}

printk(KERN_INFO "DEMO: seek move %llu is OK!\n",offset);
return filp->f_pos = pos;
}

long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case DEMO_IO_INFO:
printk(KERN_INFO "DEMO: run commod \"DEMO_IO_INFO\" successful !\n");
break;
case DEMO_IO_OK:
printk(KERN_INFO "DEMO: run commod \"DEMO_IO_OK\" successful !\n");
break;
default:
printk(KERN_INFO "DEMO: error commod %d!\n",cmd);
return -1;
break;
}
return 0;
}

//////////////////////注册销毁//////////////////////////////
int DEMO_init_module(void)
{
int result;
dev_t dev = 0;
dev = MKDEV(DEMO_MAJOR,DEMO_MINNOR);

result = register_chrdev_region(dev, 1, "DEMO");
if(result < 0)
{
printk(KERN_WARNING "DEMO: can't get major %d\n",DEMO_MAJOR);
return result;
}

DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL);
if(!DEMO_devices)
{
result = -ENOMEM;
goto fail;
}
memset(DEMO_devices, 0, sizeof(struct DEMO_dev));

cdev_init(&DEMO_devices->cdev, &DEMO_fops);
DEMO_devices->cdev.owner = THIS_MODULE;
DEMO_devices->cdev.ops = &DEMO_fops;

result = cdev_add(&DEMO_devices->cdev, dev, 1);
if (result)
{
printk(KERN_WARNING "DEMO: Error %d adding DEMO\n", result);
goto fail;
}

class_demo = class_create(THIS_MODULE, "demo_cdev");//在/sys目录下建立了一个demo_cdev的目录
if (IS_ERR(class_demo)) {
printk(KERN_INFO "DEMO: create class error\n");
return -1;
}
device_create(class_demo,NULL,dev,NULL,"demo_inode" "%d", MINOR(dev));

memset(demoBuffer,0,sizeof(demoBuffer));

printk(KERN_WARNING "DEMO:[test] add to kerner!\n");
return 0;

fail:
DEMO_cleanup_module();
return result;
}

void DEMO_cleanup_module(void)
{
dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINNOR);
unregister_chrdev_region(devno, 1);

if (DEMO_devices)
{
cdev_del(&DEMO_devices->cdev);
kfree(DEMO_devices);
device_destroy(class_demo, devno);//在2.6早期的版本应该使用class_device_destroy,早于2.6.27? 2.6.29? 忘记了。
class_destroy(class_demo);

}

printk(KERN_WARNING "DEMO:[test] remove in kerner!\n");
}

module_init(DEMO_init_module);
module_exit(DEMO_cleanup_module);
MODULE_LICENSE("GPL"); //解决 Error: could not insert module demo.ko: Unknown symbol in module


2.2 编写MakeFile编译:

[root:simple_cdev] cat Makefile
AR = ar
# ARCH = i386
CC = gcc

# 解决方法:在Makefile中将"CFLAGS"替换成"EXTRA_CFLAGS"。
EXTRA_CFLAGS += -Wall
# $(MAKE)

ifneq ($(KERNELRELEASE),)
obj-m := demo.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
@rm -rf *.o *.ko *.mod.c *.order *.symvers
endif


2.3 编译出demo.ko内核驱动模块:

[root:simple_cdev] ls

demo.c demo.h example/ Makefile

[root:simple_cdev] make

make -C /lib/modules/3.2.0-3-686-pae/build M=/work/my_test/driver/simple_cdev modules

make[1]: Entering directory `/usr/src/linux-headers-3.2.0-3-686-pae'

CC [M] /work/my_test/driver/simple_cdev/demo.o

Building modules, stage 2.

MODPOST 1 modules

CC /work/my_test/driver/simple_cdev/demo.mod.o

LD [M] /work/my_test/driver/simple_cdev/demo.ko

make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-3-686-pae'

[root:simple_cdev] ls

demo.c demo.h demo.ko demo.mod.c demo.mod.o demo.o example/ Makefile modules.order Module.symvers

[root:simple_cdev]



三.测试代码和编译:

3.1 测试代码 main.cpp 和Makfile

[user:example] cat main.cpp
/// @file main.cpp
/// @brief
//
/// @author Easton Woo
/// 0.01
/// @date 2013-09-02

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "../demo.h"

int main()
{
int fd = -1;
fd = open("/dev/demo_inode0",O_RDWR);
if(fd < 0)
{
printf("open /dev/demo_inode0 is error!\n");
return -1;
}

write(fd, "hello", sizeof("hello"));

lseek(fd, 0, SEEK_SET);

char buf[256];
memset(buf,0,sizeof(buf));
read(fd, buf, sizeof(buf));
printf("%s\n",buf);

ioctl(fd, DEMO_IO_INFO, 0);
// ioctl(fd, DEMO_IO_OK, 0);
ioctl(fd, 3, 0);

close(fd); fd = -1;

return 0;
}
[user:example]
[user:example] cat Makefile
all:
g++ -o test_demo_app.i386.elf main.cpp

.PHONY: clean
clean:
rm *.elf


3.2 编译出测试程序test_demo_app.i386.elf:
[root:simple_cdev] cd example/

[root:example] ls

main.cpp Makefile

[root:example] make

g++ -o test_demo_app.i386.elf main.cpp

[root:example] ls

main.cpp Makefile test_demo_app.i386.elf*

[root:example]



四.驱动加载内核,测试结果:

[root:simple_cdev] ls

demo.c demo.h demo.ko demo.mod.c demo.mod.o demo.o example/ Makefile modules.order Module.symvers


[root:simple_cdev] ls /dev/demo_inode0

ls: 无法访问/dev/demo_inode0: 没有那个文件或目录

[root:simple_cdev] dmesg --clear

[root:simple_cdev]

[root:simple_cdev] dmesg

[root:simple_cdev] insmod demo.ko

[root:simple_cdev] lsmod | grep 'demo'

demo 12592 0


[root:simple_cdev] ls /dev/demo_inode0

/dev/demo_inode0

[root:simple_cdev] dmesg

[170724.473397] DEMO:[test] add to kerner!

[root:simple_cdev] ./example/test_demo_app.i386.elf

hello

[root:simple_cdev] dmesg

[170724.473397] DEMO:[test] add to kerner!

[170780.200620] DEMO: device is open!

[170780.200687] DEMO: write 6 data successful

[170780.200716] DEMO: seek move 0 is OK!

[170780.200783] DEMO: read 256 data successful

[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !

[170780.201768] DEMO: error commod 3!

[170780.201779] DEMO: device is close!

[root:simple_cdev] rmmod demo

[root:simple_cdev] dmesg

[170724.473397] DEMO:[test] add to kerner!

[170780.200620] DEMO: device is open!

[170780.200687] DEMO: write 6 data successful

[170780.200716] DEMO: seek move 0 is OK!

[170780.200783] DEMO: read 256 data successful

[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !

[170780.201768] DEMO: error commod 3!

[170780.201779] DEMO: device is close!

[170816.292639] DEMO:[test] remove in kerner!

[root:simple_cdev] lsmod | grep 'demo'

[root:simple_cdev]

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: