linux驱动开发简单示例

root用户新建一个单独文件夹用于存放驱动源码和Makefile。

驱动代码zhangchen_test.c:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>


#define __DEV_NUM__ 200
static unsigned char g_a_buffer[4096];  //驱动内存,与用户空间交互使用
static struct class *g_p_class;


/* 打开设备 */
static int zhangchen_test_open(struct inode *inode, struct file *filp)
{
    /* 用户实现具体功能 */  
    printk("Enter func zhangchen_test_open open.\n");

    return 0;
}


/* 关闭/释放设备 */
static int zhangchen_test_release(struct inode *inode, struct file *filp)
{
    /* 用户实现具体功能 */
    printk("Enter func zhangchen_test_release open.\n");

    return 0;
}

 
/* 从设备读取 */
static ssize_t zhangchen_test_read(struct file *filp, char __user *buf, 
size_t count, loff_t *p_offset)
{
    /* 用户实现具体功能 */
    if(count > sizeof(g_a_buffer))
    {
        printk(KERN_ALERT "Read count is too long!\n");
        return -EMSGSIZE;
    }
    if(copy_to_user(buf,g_a_buffer+*p_offset,count))
    {
        printk(KERN_ALERT "Func copy_to_user failed!\n");
        return -EFAULT;
    }
        
    g_a_buffer[4095] = 0;
    printk("Read buffer[%s]\n" , g_a_buffer);
    

    return 0;
}

 
/* 向设备写数据 */
static ssize_t zhangchen_test_write(struct file *filp,
const char __user *buf, 
size_t count, loff_t *p_offset)
{
    /* 用户实现具体功能 */
    if(count > sizeof(g_a_buffer))
    {
        printk(KERN_ALERT "Read count is too long!\n");
        return -EMSGSIZE;
    }
    if(copy_from_user(g_a_buffer+*p_offset,buf,count))
    {
        printk(KERN_ALERT "Func copy_from_user failed!\n");
        return -EFAULT;    
    }
    
    g_a_buffer[4095] = 0;
    printk("Write buffer[%s]\n" , g_a_buffer);


    return 0;
}
   

//定义系统调用与驱动内部函数映射关系  
static struct file_operations g_test_fops = {
    .owner = THIS_MODULE,
    .open = zhangchen_test_open,
    .read = zhangchen_test_read,
    .write = zhangchen_test_write,
    .release = zhangchen_test_release,
};


/* 驱动入口函数 */
static int __init zhangchen_test_init(void)
{
    int ret = 0;
    struct device *p_device = NULL;
    printk("Enter func zhangchen_test module init!\n");    

    /* 注册字符设备驱动 */
    ret = register_chrdev(__DEV_NUM__, "zhangchen_test", &g_test_fops);  //参数1为0表示由系统分配,为其他表示自己静态分配。静态分配主设备号可从/proc/devices文件查看未使用的id(主设备号代表类型,此设备号代表该类型设备的序号)
    //注册后,可已使用命令mknod在/dev/下创建设备文件,也可以在驱动程序入口直接调用class_create、device_create创建
    if(ret < 0)
    {
        /* 字符设备注册失败,自行处理 */
        printk(KERN_ALERT "Func register_chrdev failed! Ret[%d]\n" , ret);
        return ret;                
    }
    else
    {
        //创建设备文件
        g_p_class = class_create(THIS_MODULE, "zhangchen_dev");
        if(IS_ERR(g_p_class))
        {
            printk(KERN_ALERT "Failed to create device.\n");
            unregister_chrdev(__DEV_NUM__, "zhangchen_test");
            return -1;
        }
        p_device = device_create(g_p_class, NULL, MKDEV(__DEV_NUM__, 0), NULL, "zhangchen_dev");
        if(IS_ERR(p_device))
        {
            printk(KERN_ALERT "Failed to create device.\n");
            class_destroy(g_p_class);
            unregister_chrdev(__DEV_NUM__, "zhangchen_test");
            return -2;
        }
    }
    printk("zhangchen_test module init successful!\n");
     
    return 0;
}

 
/* 驱动出口函数 */
static void __exit zhangchen_test_exit(void)
{
    printk("Enter func zhangchen_test module exit!\n");
    //注销设备文件
    device_destroy(g_p_class , MKDEV(200, 0));
    class_destroy(g_p_class);
    /* 注销字符设备驱动 */
    unregister_chrdev(__DEV_NUM__, "zhangchen_test");
    
    printk("zhangchen_test module exit successful!\n");               
}

 
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(zhangchen_test_init);
module_exit(zhangchen_test_exit);

MODULE_LICENSE("GPL");//LICENSE 采用 GPL 协议。
MODULE_AUTHOR("zhangchen");//添加作者名字

编译需要安装依赖(以centos为例):

yum -y install kernel_devel-$(uname -r)

yum -y install kernel_headers-$(uname -r)

确认/usr/src/kernels/下存在对应内核版本的目录。

(注:若yum install找不到对应版本的包,需要网页到阿里镜像站对应的过期源中下载rpm包再传到linux中手动安装。例centos7.4到https://mirrors.aliyun.com/centos-vault/7.4.1708/os/x86_64/Packages/目录下找)

Makefile:

KERNEL_DIR = /usr/src/kernels/`uname -r`

obj-m   += zhangchen_test.o

all:
    make -C $(KERNEL_DIR) M=`pwd` modules

clean:
    make -C $(KERNEL_DIR) M=`pwd` modules clean
    rm -rf *o *.mod.c *.order *.symvers

装载驱动: make编译后目录下出现对应ko,insmod zhangchen_test.ko不报错则执行成功。(也可以使用modprobe进行装载。卸载驱动为rmmod zhangchen_test.ko。)

校验结果: cat /proc/devices查看200设备id是否出现。 lsmod | grep zhangchen_test查看是否显示对应驱动。 ls /dev/查看字符设备文件zhangchen_dev有没有产生。查看/var/log/messages打印内容(驱动为zhangchen_test,设备为zhangchen_dev)

测试程序test.c

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


int main(int argc,char **argv)
{
    int fd;
    char buf[1024];
    fd = open("/dev/zhangchen_dev",O_RDWR);
    if(-1==fd)
    {
        printf("Func open failed!\n");
        return -1;
    }

    write(fd, "aabbcc" , 7);
    printf("Write [%s] to driver zhangchen_test. Please check from /var/log/messages\n" , "aabbcc");

    read(fd , buf , 7);
    printf("Read [%s] from driver zhangchen_test\n",buf);

    sleep(1);

    write(fd, "test end!" , 10);
    printf("Write [%s] to driver zhangchen_test. Please check from /var/log/messages\n" , "test end!");

    read(fd , buf , 10);
    printf("Read [%s] from driver zhangchen_test\n",buf);

    close(fd);

    puts("Exit");

    return  0;
}

猜你喜欢

转载自blog.csdn.net/jiujiederoushan/article/details/128935859
今日推荐