Linux设备驱动-秒字符设备

1. 前言

本文基于Ubuntu 22.04.2 LTS(Linux version 5.19.0-41-generic),主要是实现一个简单的字符设备驱动,以秒为单位记录时间。并且,使用应用程序验证获取当前时间。

2. 编译并加载内核驱动

//timer_cdev.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>

#define SECOND_MAJOR 155

static int second_major = SECOND_MAJOR;
module_param(second_major, int, S_IRUGO);

struct second_dev {
	struct cdev cdev;
	atomic_t counter;
	struct timer_list s_timer;
};

static struct second_dev *second_devp;

static void second_timer_handler(struct timer_list* arg)
{
	mod_timer(&second_devp->s_timer, jiffies + HZ);
	atomic_inc(&second_devp->counter);
	
	printk(KERN_ERR "current jiffies is %ld\n", jiffies);
}

static int second_open(struct inode* inode, struct file *filp)
{
#if 0
	init_timer(&second_devp->s_timer);
	second_devp->s_timer.function = &second_timer_handler;
	second_devp->s_timer.expires = jiffies + HZ;
	
	add_timer(&second_devp->s_timer);
#else
	timer_setup(&second_devp->s_timer, second_timer_handler, 0);
	add_timer(&second_devp->s_timer);
#endif

	atomic_set(&second_devp->counter, 0);  /* 初始化秒计数为0 */
	return 0;
}

static int second_release(struct inode *inode, struct file *filp)
{
	del_timer(&second_devp->s_timer);
	return 0;
}

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

static const struct file_operations second_fops = {
	.owner   = THIS_MODULE,
	.open    = second_open,
	.release = second_release,
	.read    = second_read,
};

static void second_setup_cdev(struct second_dev *dev, int index)
{
	int err, devno = MKDEV(second_major, index);
	
	cdev_init(&dev->cdev, &second_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, devno, 1);
	if (err)
		printk(KERN_ERR "Failed to add second device\n");
	
	class_create(THIS_MODULE, "second");   // 在/dev目录下创建second设备节点
}

static int __init second_init(void)
{
	printk(KERN_ERR "enter second_init, second_major:%d\n", second_major);
	int ret;
	dev_t devno = MKDEV(second_major, 0);
	
	if(second_major) {
		ret = register_chrdev_region(devno, 1, "second");
	} else {
		ret = alloc_chrdev_region(&devno, 0, 1, "second");
	}
	
	if(ret < 0) {
		printk(KERN_ERR "Failed to register chrdev\n");
		return ret;
	}
	
	second_devp = kzalloc(sizeof(*second_devp), GFP_KERNEL);
	if(!second_devp) {
		ret = -ENOMEM;
		printk(KERN_ERR "kzalloc failed\n");
		goto fail_malloc;
	}

	second_setup_cdev(second_devp, 0);
	printk(KERN_ERR "exit second_init\n");
	return 0;
	
fail_malloc:
	unregister_chrdev_region(devno, 1);
	return ret;
}

static void __exit second_exit(void)
{
	cdev_del(&second_devp->cdev);
	kfree(second_devp);
	unregister_chrdev_region(MKDEV(second_major, 0), 1);
}

module_init(second_init);
module_exit(second_exit);

MODULE_AUTHOR("littleSnail");
MODULE_LICENSE("GPL v2");

Makefile:

KVER:=$(shell uname -r)

#注意Ubuntu内核编译的gcc和指定的gcc要一致
CC:=x86_64-linux-gnu-gcc  

obj-m := timer_cdev.o

build: kernel_modules

kernel_modules:
	make CC=$(CC) -C /lib/modules/$(KVER)/build M=$(CURDIR) modules
	
clean:
	make CC=$(CC) -C /lib/modules/$(KVER)/build M=$(CURDIR) clean

执行如下命令:

make
insmod timer_cdev.ko second_major=179

执行完成后会看到字符设备**/dev/second**。

3. 编译并执行应用程序

//main.c
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
	int fd = -1;
	int ret = 0;
	unsigned long current = 0;
	unsigned long pre = 0;
	
	fd = open("/dev/second", O_RDONLY);
	if(fd < 0)
	{
		printf("open /dev/second error!\n");
		return -1;
	}
	
	while(1)
	{
		if(ret = read(fd, &current, sizeof(long)) <= 0) {
			printf("read error!\n");
			break;
		}
		
		if(current != pre) {
			printf("current:%ld\n", current);
			pre = current;
		}
	}
	
	close(fd);
	
	return ret;
}

执行命令:

x86_64-linux-gnu-gcc -g main.c -o main

执行结果如下:

root@virtual-machine:/home/timer_cdev# ./main
current:1
current:2
current:3
current:4
current:5
current:6
current:7
current:8
current:9

4. 参考文献

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》宋宝华著

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值