05 - 字符设备驱动

---- 整理自 王利涛老师 课程
实验环境:宅学部落 www.zhaixue.cc

1. 字符设备驱动开发框架

在这里插入图片描述

在这里插入图片描述

  • 实验:00rtc
// rtc.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>

static int rtc_open(struct inode *inode, struct file *fp)
{
    printk("%s...\n", __func__);
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    printk("%s...\n", __func__);
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    printk("%s...\n", __func__);
    return 0;
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    printk("%s...\n", __func__);
    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    ret = register_chrdev(222, "rtc-demo", &rtc_fops);
    if (ret < 0) {
        printk("Register char module: rtc failed..\n");
        return 0;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
    
    return 0;
}

static void __exit rtc_exit(void)
{
    printk("Goodbye char module: rtc!\n");
    unregister_chrdev(222,"rtc-demo");
}

module_init(rtc_init);
module_exit(rtc_exit);

ifneq ($(KERNELRELEASE),)
obj-m := rtc.o

else
EXTRA_CFLAGS += -DDEBUG 
KDIR := /home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    close(fd);

    return 0;
}

在这里插入图片描述

2. 编写第一个字符驱动:RTC

  • 实验:01rtc
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int count = 0;

    return count;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = register_chrdev(222, "rtc-demo", &rtc_fops);
    if (ret < 0) {
        printk("Register char module: rtc failed..\n");
        return 0;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
    
    return 0;
}

static void __exit rtc_exit(void)
{
    printk("Goodbye char module: rtc!\n");
    unregister_chrdev(222,"rtc-demo");
}

module_init(rtc_init);
module_exit(rtc_exit);

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    close(fd);

    return 0;
}

在这里插入图片描述

3. 字符设备的注册过程

在这里插入图片描述

  • register_chrdev() 注册流程
  1. 申请设备号:__register_chrdev_region
  2. 创建 cdev 对象:cdev_alloc
  3. 初始化 cdev 对象:cdev->ops = fops
  4. 将 cdev 添加到驱动模型中:cdev_add,kboj、kset
  5. 将 cdev 和 chrdevs 数组中的 char_device_struct 建立关联:cd->cdev = cdev

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用 cdev 结构体 来描述一个字符设备
在这里插入图片描述

  • 内核给出的操作 struct cdev 结构的接口主要有以下几个:
  1. cdev_init,该函数主要对 struct cdev 结构体做初始化, 最重要的就是建立 cdev 和 file_operations 之间的连接。

在这里插入图片描述

  1. cdev_alloc,该函数主要分配一个 struct cdev 结构,动态申请一个 cdev 内存,并做了 cdev_init 中所做的前面 3 步初始化工作(第 4 步初始化工作需要在调用 cdev_alloc 后,显式的做初始化,即:.ops=xxx_ops)。

在这里插入图片描述

  1. cdev_add,该函数向内核注册一个 struct cdev 结构,即正式通知内核由 struct cdev *p 代表的字符设备已经可以使用了。
  2. cdev_del,该函数向内核注销一个 struct cdev 结构,即正式通知内核由 struct cdev *p 代表的字符设备已经不可以使用了。

4. 字符设备的 open 和 read 过程

mknod:每创建一个文件,文件系统就会分配一个 inode,文件与 inode 一一对应

// kernel\linux-5.10.4\include\linux\fs.h
struct inode { // 内核中所有文件都使用inode来表示
	umode_t			i_mode;
	unsigned short	i_opflags;
	kuid_t			i_uid; // inode拥有者ID
	kgid_t			i_gid; // inode所属群组ID
	unsigned int	i_flags; // 文件类型

	...

	dev_t			i_rdev; // 若是设备文件,表示设备号
	
	...
	
	struct list_head	i_devices; // 多个设备文件可以共用同一个驱动程序,通过字符设备的inode中的i_devices和cdev中的list组成一个链表
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev; // <== 这里
		char			*i_link;
		unsigned		i_dir_seq;
	};

	...
} __randomize_layout;

在这里插入图片描述

  • open("dev/rtc-demo0", O_RDWR);
  1. 通过系统调用,调用 sys_open,根据设备名对应的 inode 信息 i_flags 找到设备类型,创建 file
  2. 根据设备号 (inode->i_rdev) 查找 cdev:kboj_lookup
  3. 保存找到的 cdev:inode->i_cdev = cdev
  4. 将 inode 添加到 cdev->list 链表中
  5. 用 cdev 中的 ops 初始化 file:file->f_op = cdev->ops
  6. 调用 open 回调函数:file->f_op->open(inode, filp)
  7. open 执行完成,通过 VFS 返回给用户空间一个文件描述符:fd
    fd 和内核中的 file 相对应,上层通过 fd 就可以找到 file,找到对应的 read、write 函数

5. 使用 cdev 接口编写字符驱动

  • 实验:02rtc
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>  // <==这里

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}


static dev_t dev;
static struct cdev *rtc_cdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int count = 0;

    return count;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    dev = MKDEV(222, 0); // <==这里,通过major和minor构建设备号,MKDEV(int major,int minor);

    rtc_cdev = cdev_alloc(); // <==这里
    cdev_init(rtc_cdev, &rtc_fops); // <==这里

    register_chrdev_region(dev, 1, "rtc-demo"); // <==这里,静态申请设备号
    ret = cdev_add(rtc_cdev, dev, 1); // <==这里
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
    
    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_cdev); // <==这里
    unregister_chrdev_region(dev, 1);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);

在这里插入图片描述

6. 静态申请和动态申请设备号

6.1 构建设备号

  • 每个设备都有设备号:主设备号(12位)+ 次设备号(20)=(32位)
    一个字符设备或块设备都有一个主设备号和一个次设备号。主设备号用来标识与设备文件相连的驱动程序,用来反映设备类型。次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备。
  1. 从设备号中提取 major 和 minor
MAJOR(dev);                              
MINOR(dev);
  1. 通过 major 和 minor 构建设备号
MKDEV(ma, mi);

注:这只是构建设备号。并未注册,需要调用 register_chrdev_region 静态申请(前面实验中)。

#define MINORBITS	20
#define MINORMASK	((1U << MINORBITS) - 1)

#define MAJOR(dev)	((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)	((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)	(((ma) << MINORBITS) | (mi))

6.2 分配设备号

6.2.1 静态申请

在这里插入图片描述

6.2.2 动态分配

在这里插入图片描述

6.2.3 分析

  • 可以看到静态申请和动态分配二者都是调用了 __register_chrdev_region 函数:

在这里插入图片描述
在这里插入图片描述

  • register_chrdev_regionalloc_chrdev_region 的区别,register_chrdev_region 直接将 Major 注册进入,而 alloc_chrdev_region 从 Major = 0 开始,逐个查找设备号,直到找到一个闲置的设备号,并将其注册进去。
register_chrdev_regionalloc_chrdev_regionregister_chrdev
devno = MKDEV(major,minor);
ret = register_chrdev_region(devno, 1, "xxx");
cdev_init(&cdev,&xxx_ops);
ret = cdev_add(&cdev,devno,1);
alloc_chrdev_region(&devno, minor, 1, "xxx");
major = MAJOR(devno);
cdev_init(&cdev,&xxx_ops);
ret = cdev_add(&cdev,devno,1)

6.3 实验

  • 实验:03rtc
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h> // <==这里

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}


static dev_t devno; // <==这里
static struct cdev *rtc_cdev; // <==这里

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int count = 0;

    return count;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo"); // <==这里。更简便、更智能的方法是让内核给我们自动分配一个主设备号,
    													 // 使用alloc_chrdev_region就可以自动分配了。
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc(); // <==这里
    cdev_init(rtc_cdev, &rtc_fops); // <==这里

    ret = cdev_add(rtc_cdev, devno, 1); // <==这里
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
    
    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_cdev); // <==这里
    unregister_chrdev_region(devno, 1);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);

在这里插入图片描述

7. 自动创建设备结点

  • alloc_chrdev_region:动态设备号,返回的设备号不一定是固定的
  • udev:运行在用户空间,用来 自动创建 设备节点:/dev/xxx。内核中定义的 struct class 结构体,顾名思义,一个 struct class 结构体类型变量对应一个类,内核同时提供了 class_create(…) 函数,可以用它来创建一个类,这个类存放于 sysfs 下面,一旦创建好了这个类,再调用 device_create(…) 函数来在 /dev 目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的 udev 会自动响应 device_create() 函数,去 /sysfs 下寻找对应的类从而创建设备节点。
  • mdev:busybox
  • 内核:class_create、device_create(在驱动初始化代码里调用 class_create 为该设备创建一个 class,再为每个设备调用 device_create 创建对应的设备)
  • 实验:04
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}


static dev_t devno;
static struct cdev *rtc_cdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int count = 0;

    return count;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc();
    cdev_init(rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_cdev, devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }
	
	// 宏class_create()用于动态创建设备的逻辑类,并完成部分字段的初始化,然后将其添加进Linux内核系统中。
	// 此函数的执行效果就是在/sys/class/目录下创建一个新的文件夹rtc-class,此文件夹的名字为此函数的第二个输入参数rtc-class
    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }
    
	// device_create会在/dev目录下生成rtc-demo0的设备文件
    rtc_device = device_create(rtc_class, NULL, devno, NULL, "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_cdev);
    unregister_chrdev_region(devno, 1);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL"); // 需要加LICENSE
MODULE_AUTHOR("uuuuu");


在这里插入图片描述
在这里插入图片描述

  • mdev -s:在系统启动时,通过执行“mdev -s”扫描 /sys/class/sys/block,在目录中查找 dev 文件。例如:/sys/class/rtc-class/rtc-demo0/dev,它的内容为 “250:0”,即主设备号是 250,次设备号是 0,dev 的上一级目录为设备名,这里是 rtc-demo0。/sys/class 下的每个文件夹都代表着一个子系统。
  • mdev -d:自动扫描 /sys/class/sys/block
    注:mdev 是 busybox 提供的一个工具,用在嵌入式系统中,相当于 简化版的 udev,作用是在系统启动和热插拔或动态加载驱动程序时,自动创建设备节点。文件系统中的 /dev 目录下的设备节点都是由 mdev 创建的。
  • 补充:我们可以修改系统的启动脚本 rcS,添加 mdev -d。系统启动后,不需要手动执行 mdev -s,就可以在 /dev 目录下创建设备节点 rtc-demo0。

在这里插入图片描述

这样,我们不再需要自己手动创建节点。

8. 实现字符驱动接口:write

  • 实验:05rtc
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    close(fd);

    return 0;
}

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm; // <==here
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}

static void rtc_tm_to_time(void)
{
    cur_time = tm.hour * 3600 + tm.min * 60 + tm.sec;
}

static dev_t devno;
static struct cdev *rtc_cdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){ // <==here
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) { // <==here
        printk("rtc_write error!\n");
        return -1;
    }
    rtc_tm_to_time();
    regs->RTCLR = cur_time;

    return len;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(devno), MINOR(devno));

    rtc_cdev = cdev_alloc();
    cdev_init(rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_cdev, devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL, devno, NULL, "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_cdev);
    class_destroy(rtc_class);
    unregister_chrdev_region(devno, 1);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("uuuuu");

9. 设备驱动的私有数据:private_data

注:当驱动复杂时,不方便访问如 rtc_chrdev 这个结构体,可以通过 private_data 这种方法。

  • 实验:06rtc
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_char_device {
    struct cdev *rtc_cdev;
    dev_t devno;
}; // <==here

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(void)
{
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec = cur_time % 60;
}

static void rtc_tm_to_time(void)
{
    cur_time = tm.hour * 3600 + tm.min * 60 + tm.sec;
}

static struct rtc_char_device rtc_chrdev; // <==here

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev; // 在 Linux 内核中,file 结构体用于表示一个打开的文件,其中包含了对该文件的各种操作和状态信息。
                                    // 当打开一个字符设备时,系统会创建一个 file 结构体来表示这个打开的设备文件。
                                    // private_data 是 file 结构体中的一个字段,它可以用来存储用户定义的数据指针。
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_char_device *p = fp->private_data; // <==here
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate();
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) {
        printk("rtc_write error!\n");
        return -1;
    }
    rtc_tm_to_time();
    regs->RTCLR = cur_time;

    return len;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("uuuuu");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    close(fd);

    return 0;
}

在这里插入图片描述

10. 实现字符驱动接口:ioctl

  • 实验:07rtc
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/ioctl.h>

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, 1, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    ret = ioctl(fd, 2, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    ret = ioctl(fd, 3, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }

    close(fd);

    return 0;
}

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_char_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

#define RTC_BASE 0x10017000

static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static struct rtc_char_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_char_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
        case 1:
            printk("cmd = 1\n");
            if (copy_from_user(&tm, (struct rtc_time __user *)arg, sizeof(tm)))
                return -EFAULT;
            set_rtc_time(&tm);
            break;
        case 2:
            printk("cmd = 2\n");
            break;
        case 3:
            printk("cmd = 3\n");
            break;
        default:
            printk("error cmd!\n");
            return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("uuuuu");

在这里插入图片描述

  • 遗留问题:为什么 cmd=1 和 cmd=3 打印出来了,cmd=2 没有打印出来?
  • 需要命令编码规范

11. ioctl 命令编码规范

+------+------+------+---------+
| 8bit | 8bit | 2bit | 8~14bit |
+------+------+------+---------+
| type | nr   | dir  | size    |
+------+------+------+---------+

cmd 命令的宏:

#define _IO(type,nr)  _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)	  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)	  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOC_TYPECHECK(size) sizeof(size)
  • 实验:08rtc
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }

    close(fd);

    return 0;
}
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_char_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)


static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static struct rtc_char_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_char_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (copy_from_user(&tm, buf, len) != 0) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}

static void set_rtc_alarm(struct rtc_time *t)
{
    unsigned long tmp = 0;
    
    tmp = regs->RTCCR;
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = rtc_tm_to_time(t);
    regs->RTCMR = tmp;

    regs->RTCICR = 1;
    regs->RTCIMSC = 1;

    tmp = regs->RTCCR;
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    switch (cmd) {
        case RTC_SET_TIME:
            printk("cmd = 1\n");
            if (copy_from_user(&tm, buf, sizeof(tm)))
                return -EFAULT;
            set_rtc_time(&tm);
            break;
        case RTC_SET_ALARM:
            printk("cmd = 2\n");
            if (copy_from_user(&tm, buf, sizeof(tm)))
                return -EFAULT;
            set_rtc_alarm(&tm);
            break;
        default:
            printk("error cmd!\n");
            return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{

    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    printk("\nalarm: beep~ beep~ \n");
    printk("\n    %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    regs->RTCICR = 1;
    return IRQ_HANDLED;
}

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("uuuuu");

在这里插入图片描述

12. 让你的驱动更加稳定高效

  • 检查ioctl命令
    _IOC_TYPE(cmd):判断应用程序传下来的命令 type 是否正确
    _IOC_DIR(cmd):判断命令是读还是写
  • 检查用户层传递的内存地址是否合法
    access_ok(addr, size):判断用户层传递的内存地址是否合法
    返回值:1 - 成功,0 - 失败
    有些函数内部自带检测:copy_from_user,copy_to_user,get_user,put_user
    有些没有,需要用户自己检测:__get_user,__put_user
  • 分支预测优化:likely,unlikely
    CPU cache 和流水线结构
  • 实验:09rtc
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST  _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    
    /* ioctl invalid cmd test */
    ret = ioctl(fd, RTC_SET_TEST, (unsigned long)&tm);
    if (ret) {
        printf("main: ioctl failed!\n");
        return -1;
    }

    close(fd);

    return 0;
}
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
}rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_char_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)


static volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static struct rtc_char_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_char_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{

    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}

static void set_rtc_alarm(struct rtc_time *t)
{
    unsigned long tmp = 0;
    
    tmp = regs->RTCCR;
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = rtc_tm_to_time(t);
    regs->RTCMR = tmp;

    regs->RTCICR = 1;
    regs->RTCIMSC = 1;

    tmp = regs->RTCCR;
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }
    switch (cmd) {
        case RTC_SET_TIME:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc: invalid ioclt cmd!\n");
                return -EINVAL;
            }
            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }
            set_rtc_time(&tm);
            break;
        case RTC_SET_ALARM:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc ioctl: invalid ioclt cmd!\n");
                return -EINVAL;
            }
            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }
            set_rtc_alarm(&tm);
            break;
        default:
            printk("error cmd!\n");
            return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{

    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    printk("\nalarm: beep~ beep~ \n");
    printk("\n    %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    regs->RTCICR = 1;
    return IRQ_HANDLED;
}

static int __init rtc_init(void)
{
    int ret = 0;
    
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t));
    printk("rtc_init\n");

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    }
    else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    return 0;
}

static void __exit rtc_exit(void)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
    printk("Goodbye char module: rtc!\n");
}

module_init(rtc_init);
module_exit(rtc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("uuuuu");

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uuxiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值