Linux字符驱动入门

概要

Linux字符驱动入门,以Debian作为示例

准备工作

需要安装下面软件

$ sudo apt install -y gcc make linux-headers-$(uname -r)
软件名作用
gcc编译器
make自动化编译工具
linux-headers-$(uname -r)linux头文件

字符驱动源码

下面源码保存到chrdev.c

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */
#include <linux/uaccess.h>      /* copy_from_user & copy_to_user */
#include <linux/fs.h>           /* file_operations */
#include <linux/cdev.h>         /* cdev */

#define DEV_NAME "chardev"
#define DEV_CNT 2
#define BUFF_SIZE 128

//定义字符设备的设备号
static dev_t devno;

struct mychr
{
    struct cdev chr_dev;
    char vbuf[BUFF_SIZE];
    char flag;
};
static struct mychr __mychr[DEV_CNT]; 

static int chr_dev_open(struct inode *inode, struct file *filp)
{
    printk("open\n");

    // container_of可以通过结构体的子元素找到结构体的基地址
    // inode->i_cdev保持设备的cdev
    filp->private_data = container_of(inode->i_cdev, struct mychr, chr_dev);

    return 0;
}

static int chr_dev_release(struct inode *inode, struct file *filp)
{
    printk("release\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;

    struct mychr *dev = filp->private_data;
    char *vbuf = dev->vbuf;

    if(p > BUFF_SIZE)
        return 0;

    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;

    struct mychr *dev = filp->private_data;
    char *vbuf = dev->vbuf;

    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 struct file_operations chr_dev_fops = 
{
    .owner = THIS_MODULE,
    .open = chr_dev_open,
    .release = chr_dev_release,
    .read = chr_dev_read,
    .write = chr_dev_write,
};

static int __init chrdev_init(void)
{
    int ret = 0;
    unsigned char i = 0;
    printk("chrdev init\n");

    // 第一步
    // 采用动态分配得分方式,获取设备编号,次设备号为0
    // 设备名称为 DEV_NAME,可通过命令 cat /proc/devices 查看
    // DEV_CNT为申请设备编号数量
    ret = alloc_chrdev_region(&devno, 0, DEV_CNT, DEV_NAME);
    if(ret < 0)
    {
        printk("fail to alloc devno\n");
        goto alloc_err;
    }
    
    // 第二步
    // 关键字符设备结构体 cdev 与 文件操作结构体 file_operations
    
    // 第三步
    // 添加设备至cdev_map 散列表中
    for(i = 0; i < DEV_CNT; i ++)
    {
        cdev_init(&__mychr[i].chr_dev, &chr_dev_fops);

        ret = cdev_add(&__mychr[i].chr_dev, devno + i, 1);
        if (ret < 0 )
        {
            printk("fail to add cdev\n");
            goto add_err;
        }

        __mychr[i].flag = 1;
    }

    return 0;

add_err:
    for(i = 0; i < DEV_CNT; i ++)
    {
        if(__mychr[i].flag == 1)
        {
            cdev_del(&__mychr[i].chr_dev);
        }

        __mychr[i].flag = 0;
    }

    // 添加设备失败时,需要注销设备号
    unregister_chrdev_region(devno, DEV_CNT);

alloc_err:
    return ret;
}

static void __exit chrdev_exit(void)
{
    unsigned char i = 0;
    printk("chrdev exit\n");
    unregister_chrdev_region(devno, DEV_CNT);

    for(i = 0; i < DEV_CNT; i ++)
    {
        cdev_del(&__mychr[i].chr_dev);

        __mychr[i].flag = 0;
    }
}

module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL2");

Makefile

保存到Makefile

obj-m := chrdev.o
KDIR  := /lib/modules/$(shell uname -r)/build

all:
	make -C $(KDIR) M=$$PWD
	
clean:
	rm chrdev.mod.c chrdev.o modules.order chrdev.mod.o Module.symvers

编译

执行如下命令

$ make

安装

$ sudo insmod chrdev.ko
$ cat /proc/devices
   Character devices: 
       243 chardev
$ sudo mknod /dev/chardev1 c 243 0
$ sudo mknod /dev/chardev2 c 243 1     

测试

$ echo "1111"  > /dev/chardev1
$ echo "2222"  > /dev/chardev2
$ cat /dev/chardev1
1111
$ cat /dev/chardev2
2222
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值