字符设备驱动

字符设备驱动

1 代码

1.1 驱动代码

char_dev.c

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


#define BUFF_SIZE               128

//定义一个宏表示设备名字
#define dev_name "char_device_name"

//定义宏表示设备数量
#define dev_count (1)

//定义字符设备号(普通定义)
static dev_t dev_number;

//定义字符设备结构体
static struct cdev chr_dev;


//数据缓冲区
static char vbuf[BUFF_SIZE];
static int chr_dev_open(struct inode *inode, struct file *filp);
static int chr_dev_release(struct inode *inode, struct file *filp);
static ssize_t chr_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos);
static ssize_t chr_dev_read(struct file *filp, char __user * buf, size_t count, loff_t *ppos);
static struct file_operations  chr_dev_fops = 
{
    .owner = THIS_MODULE,
    .open = chr_dev_open,
    .release = chr_dev_release,
    .write = chr_dev_write,
    .read = chr_dev_read,
};

static int chr_dev_open(struct inode *inode, struct file *filp)
{
    printk("\nopen\n");
    return 0;
}

static int chr_dev_release(struct inode *inode, struct file *filp)
{
    printk("\nrelease\n");
    return 0;
}

static ssize_t chr_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos)
{
    unsigned long p = *ppos;
    int ret;
    int tmp = count ;
    if(p > BUFF_SIZE)
        return 0;
    if(tmp > BUFF_SIZE - p)
        tmp = BUFF_SIZE - p;
    ret = copy_from_user(vbuf, buf, tmp);
    *ppos += tmp;
    return tmp;
}

static ssize_t chr_dev_read(struct file *filp, char __user * buf, size_t count, loff_t *ppos)
{
    unsigned long p = *ppos;
    int ret;
    int tmp = count ;
    static int i = 0;
    i++;
    if(p >= BUFF_SIZE)
        return 0;
    if(tmp > BUFF_SIZE - p)
        tmp = BUFF_SIZE - p;
    ret = copy_to_user(buf, vbuf+p, tmp);
    *ppos +=tmp;
    return tmp;
}


static int __init char_device_init(void)
{
    int ret = 0;
    printk("char_device init sucess!\n");
    
    //采用动态分配的方式,获取主设备编号,次设备号为0,设备名称和设备数量
    //设备名称(char_device_name)可通过命令cat /proc/devices查看
   
    ret = alloc_chrdev_region(&dev_number, 0, dev_count, dev_name);
    if (ret < 0) {
        printk("fail to alloc devno\n");
        goto alloc_err;
    }

   
   //字符设备结构体cdev与文件操作结构体file_operations关联起来
    cdev_init(&chr_dev, &chr_dev_fops);

   
   //添加设备至cdev_map散列表中
    ret = cdev_add(&chr_dev, dev_number, dev_count);
    if (ret < 0) {
        printk("fail to add cdev\n");
        goto add_err;
    }
   
   return 0;

add_err:
   //添加设备失败时,注销设备号
   unregister_chrdev_region(dev_number, dev_count);

alloc_err:
   return ret;

}

static void __exit char_device_exit(void)
{

    printk("char_device exit!\n");
    //注销设备号
    unregister_chrdev_region(dev_number,dev_count);
    //将cdev结构体从内核中移除
    cdev_del(&chr_dev);

}


module_init(char_device_init);
module_exit(char_device_exit);
MODULE_LICENSE("GPL");

1.2 应用代码

chardev_test.c代码,用来测试驱动程序。

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

char *wbuf = "Hello World\n";
char rbuf[128];

int main(void)
{
   printf("My char_dev test\n");
   //打开文件
   int fd = open("/dev/chrdev", O_RDWR);
   //写入数据
   write(fd, wbuf, strlen(wbuf));
   //写入完毕,关闭文件
   close(fd);
   //打开文件
   fd = open("/dev/chrdev", O_RDWR);
   //读取文件内容
   read(fd, rbuf, 128);
   //打印读取的内容
   printf("The content : %s", rbuf);
   //读取完毕,关闭文件
   close(fd);
   
   return 0;
}

1.3 Makefile文件

#这里具体要根据当前文件和内核所在目录的位置设定
KERNEL_DIR=../ebf_linux_kernel/build_image/build

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

obj-m := chrdev.o
out =  chrdev_test

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
	$(CROSS_COMPILE)gcc -o $(out) chrdev_test.c

.PHONY:clean
clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
	rm $(out)

2 程序运行结果

2.1 编译文件

image-20221125161226259

使用make命令使用Makefile编译文件

make

将编译好的chrdev_test和chrdev.ko文件,通过NFS移动到开发板

image-20221125160700066

2.2 加载驱动模块

使用命令加载模块,可以看到我们驱动文件编写时设置的打印信息char_device init sucess!

sudo insmod /mnt/chrdev.ko

image-20221125124058069

驱动模块加载成功后,在/proc/devices目录下,会生成一个字符设备char_device_name,主设备号为244。

cat /proc/devices

image-20221125124521655

2.3 创建设备文件

根据注册到内核的设备,可以看到主设备号为244,使用mkmod命令创建设备文件

sudo mknod /dev/chrdev c 244 0

2.4 运行测试程序

运行chrdev_test可执行文件,这里我将chrdev_test文件拷贝到当前目录了。

sudo ./chrdev_test

执行效果如下

image-20221125151746208

2.5 删除内核模块和设备文件

卸载内核模块

sudo rmsmod chrdev.ko

image-20221125153647264

内核模块卸载后,这时候再查看,char_device_name的字符设备就没有了

cat /proc/devices

image-20221125155038791

删除设备文件,这个文件是之前通过mknod命令创建的。

sudo rm /dev/chrdev
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux字符设备驱动实验是指在Linux操作系统中编写和测试字符设备驱动程序的过程。字符设备驱动程序负责与字符设备进行交互,包括输入输出数据、控制设备和处理设备的状态等。 在进行Linux字符设备驱动实验之前,首先需要了解字符设备字符设备驱动的基本概念及其工作原理。字符设备是指以字符为单位进行输入输出的设备,如串口、打印机等。字符设备驱动是指将操作系统与字符设备进行交互的程序。 在实验中,我们通常需要编写一个字符设备驱动程序,包括初始化设备、读写数据、控制设备等功能。首先,我们需要定义字符设备驱动的数据结构,包括设备号、驱动程序打开、关闭等函数的实现。然后,我们需要实现字符设备驱动的读写函数来实现数据的输入输出。最后,我们可以进行一些附加功能的实现,如控制设备的状态、处理中断等。 在实验过程中,我们需要使用Linux内核提供的字符设备接口来进行字符设备驱动的编写和测试。可以使用一些工具和命令来加载和测试字符设备驱动程序,如insmod、rmmod等。通过这些工具和命令,我们可以加载和卸载字符设备驱动程序,并在用户空间进行数据的读写操作,来测试字符设备驱动的功能和性能。 Linux字符设备驱动实验可以帮助我们深入了解字符设备字符设备驱动的工作原理,并学习Linux内核的开发和调试技术。通过实验,我们可以更好地理解操作系统和驱动程序之间的关系,提高我们在Linux系统开发和嵌入式系统开发中的能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值