Linux 驱动学习笔记3 -- 字符设备驱动实例(driver+client)

字符设备驱动实例


有了上一节的基础,下面学习一下如何编写一个字符设备驱动,并通过客户端测试,验证字符设备驱动是否创建成功

1、字符设备驱动程序

下面是字符设备驱动源码

borytest.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <asm/atomic.h>
#include <linux/slab.h>

#define BORY_MAJOR 255  /*预设bory的主设备号*/

static int bory_major = BORY_MAJOR;

struct bory_dev	/*设备号结构体*/
{
	struct cdev cdev;
	atomic_t counter;
	struct timer_list s_timer;
};

struct bory_dev *bory_devp;	/*设备结构体指针*/

static void bory_timer_handle(unsigned long arg)	/*定时器处理函数*/
{
	printk(KERN_NOTICE "======== bory_timer_handle ");
	mod_timer(&bory_devp->s_timer, jiffies + HZ);
	atomic_inc(&bory_devp->counter);
	printk(KERN_ERR "current jiffies is %ld\n", jiffies);
}

int bory_open(struct inode *inode, struct file *filp)	/*文件打开函数*/
{
	printk(KERN_NOTICE "======== bory_open ");
	init_timer(&bory_devp->s_timer);
	bory_devp->s_timer.function = &bory_timer_handle;
	bory_devp->s_timer.expires = jiffies + HZ;
	add_timer(&bory_devp->s_timer);	/*注册定时器*/
	atomic_set(&bory_devp->counter, 0);
	return 0;
}

int bory_release(struct inode *inode, struct file *filp)	/*文件释放*/
{
	printk(KERN_NOTICE "======== bory_release ");	
	del_timer(&bory_devp->s_timer);
	return 0;
}

static ssize_t bory_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
	printk(KERN_NOTICE "======== bory_read ");	
	int counter;
	counter = atomic_read(&bory_devp->counter);
	if(put_user(counter, (int*)buf))
	{
		return -EFAULT;
	}else
	{
		return sizeof(unsigned int);
	}
}

/* 文件操作结构体*/
static const struct file_operations bory_fops =
{
	.owner = THIS_MODULE,
	.open = bory_open,
	.release = bory_release,
	.read = bory_read,
};

/*初始化并注册cdev*/
static void bory_setup_cdev(struct bory_dev *dev, int index)
{
	printk(KERN_NOTICE "======== bory_setup_cdev 1");	
	int err, devno = MKDEV(bory_major, index);
	printk(KERN_NOTICE "======== bory_setup_cdev 2");	
	cdev_init(&dev->cdev, &bory_fops);
	printk(KERN_NOTICE "======== bory_setup_cdev 3");	
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &bory_fops;
	printk(KERN_NOTICE "======== bory_setup_cdev 4");	
	err = cdev_add(&dev->cdev, devno, 1);
	printk(KERN_NOTICE "======== bory_setup_cdev 5");
	if(err)
	{
		printk(KERN_NOTICE "Error %d add bory %d", err, index);	
	}
}

int bory_init(void)
{
	printk(KERN_NOTICE "======== bory_init ");	
	int ret;
	dev_t devno = MKDEV(bory_major, 0);
	/*申请设备号*/
	if(bory_major)
	{
		printk(KERN_NOTICE "======== bory_init 1");
		ret = register_chrdev_region(devno, 1, "bory");
	}else
	{
		printk(KERN_NOTICE "======== bory_init 2");
		ret = alloc_chrdev_region(&devno,0,1,"bory");
		bory_major = MAJOR(devno);
	}
	if(ret < 0)
	{
		printk(KERN_NOTICE "======== bory_init 3");
		return ret;
	}
	/*动态申请设备结构体内存*/
	bory_devp = kmalloc(sizeof(struct bory_dev), GFP_KERNEL);
	if(!bory_devp)	/*申请失败*/
	{
		ret = -ENOMEM;
		printk(KERN_NOTICE "Error add bory");	
		goto fail_malloc;
	}

	memset(bory_devp,0,sizeof(struct bory_dev));
	printk(KERN_NOTICE "======== bory_init 3");
	bory_setup_cdev(bory_devp, 0);
	printk(KERN_NOTICE "======== bory_init 4");
	return 0;

	fail_malloc:
		unregister_chrdev_region(devno,1);
}

void bory_exit(void)	/*模块卸载*/
{
	printk(KERN_NOTICE "End bory");	
	cdev_del(&bory_devp->cdev);	/*注销cdev*/
	kfree(bory_devp);		/*释放设备结构体内存*/
	unregister_chrdev_region(MKDEV(bory_major,0),1);	//释放设备号
}

MODULE_AUTHOR("BORY");
MODULE_LICENSE("Dual BSD/GPL");
module_param(bory_major, int, S_IRUGO);
module_init(bory_init);
module_exit(bory_exit);

下面是Makefile文件

ifneq ($(KERNELRELEASE),)
obj-m := borytest.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

然后在所在目录下敲命令make,ls可以察看一下文件

bory@borya:~/driver/timertest2$ ls
borytest.c   borytest.mod.c  borytest.o  Makefile~      Module.symvers
borytest.ko  borytest.mod.o  Makefile    modules.order

装载borytest.ko

bory@borya:~/driver/timertest2$ sudo insmod ./borytest.ko

使用dmesg命令查看打印出的log信息

bory@borya:~/driver/timertest2$ dmesg | tail -10
[26341.765552] End second
[26586.476212] ======== bory_init 
[26586.476216] ======== bory_init 1
[26586.476219] ======== bory_init 3
[26586.476220] ======== bory_setup_cdev 1
[26586.476221] ======== bory_setup_cdev 2
[26586.476223] ======== bory_setup_cdev 3
[26586.476224] ======== bory_setup_cdev 4
[26586.476227] ======== bory_setup_cdev 5
[26586.476228] ======== bory_init 4

此时同样可以使用lsmod命令查看上面的字符设备是否已经加载成功

bory@borya:~/driver/timertest2$ lsmod | head -5
Module                  Size  Used by
borytest               12714  0 
hello                  12448  0 
nls_utf8               12493  1 
isofs                  39549  1

2、客户端测试程序

好了,字符设备驱动已经完成,下面写个客户端测试程序

test.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
	int fd,i;
	int data;
	fd = open("/sys/module/borytest", O_RDONLY);/*打开/dev/second设备文件*/
	if (fd < 0)
	{	
		printf("open /dev/borytest error\n");
	} else 
	{
		printf("open /dev/borytest success\n");
	}
	
	close(fd);
}
用命令gcc -o test.o testc编译

bory@borya:~/driver/test$ gcc -o test.o test.c
bory@borya:~/driver/test$ ./test.o 
open /dev/borytest success

看到打印结果了,说明打开字符设备成功!


3、出错处理

加载字符设备驱动时,可能会出现 "Device or resource busy"的错误,这很可能是因为你定义的字符设备号已经被使用了,需要修改

#define BORY_MAJOR 255
的值。

如何查看字符设备号是否被使用了呢,使用下面的命令

bory@borya:~/driver/test$ cat /proc/devices 
Character devices:
255 bory
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  5 ttyprintk
  6 lp
  7 vcs
 10 misc
 13 input
 21 sg
 29 fb
 99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
251 hidraw
252 usbmon
253 bsg
254 rtc

Block devices:
  1 ramdisk
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
253 device-mapper
254 mdp

可以看到,上面有
255 bory
如果下次写字符设备驱动时,就不能再使用255设备号了。




  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值