【linux】字符设备驱动

  1. 驱动代码
  2. Makefile
  3. 编译和加载驱动
  4. 编写应用程序测试驱动

驱动程序

// hello.c 驱动程序
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define BUFFER_MAX    (10)
#define OK            (0)
#define ERROR         (-1)

struct cdev *gDev;
struct file_operations *gFile;
dev_t  devNum;
unsigned int subDevNum = 1;
int reg_major  =  232;     // 主设备号 标识一类设备
int reg_minor =   0;       // 次设备号 标识该类设备的哪个设备
char *buffer;

int hello_open(struct inode *p, struct file *f)
{
    printk(KERN_INFO "hello_open\r\n");
    return 0;
}

ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{
    printk(KERN_INFO "hello_write\r\n");
    return 0;
}

ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{
    printk(KERN_INFO "hello_read\r\n");      
    return 0;
}

int hello_init(void)
{
    devNum = MKDEV(reg_major, reg_minor); // 根据主次设备号生成一个设备号(为一个设备一个设备号)
    if(OK == register_chrdev_region(devNum, subDevNum, "helloworld")){  // 将该设备注册到内核
        printk(KERN_INFO "register_chrdev_region ok \n"); 
    }else {
    printk(KERN_INFO "register_chrdev_region error n");
        return ERROR;
    }
    printk(KERN_INFO " hello driver init \n");

    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);  // 申请一个cdev结构体,用于描述设备
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL); // file_operations是设备的操作的抽象函数

// 下面就是将自定义的操作函数赋予该设备的操作列表中
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;

    cdev_init(gDev, gFile); // 建立dev和操作的联系
    cdev_add(gDev, devNum, 1); // 建立设备号和dev结构体联系
    return 0;
}

void __exit hello_exit(void)
{
  printk(KERN_INFO " hello driver exit \n");
    cdev_del(gDev);
    kfree(gFile);
    kfree(gDev);
    unregister_chrdev_region(devNum, subDevNum);
    return;
}

module_init(hello_init); // 驱动程序的入口函数,相当于main函数,当insmod就会调用hello_init函数
module_exit(hello_exit); // 当rmmod时,调用hello_exit
MODULE_LICENSE("GPL"); 

Makefile

ifneq ($(KERNELRELEASE),)
    obj-m := helloDev.o
else
PWD := $(shell pwd)
KDIR := /lib/modules/4.4.0-31-generic/build # 内核头文件的目录,如果没有这个目录,需要安装内核开发包
#KDIR := /lib/modules/`uname -r`/build
all:
    make -C $(KDIR) M=$(PWD)
clean:
    rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~
endif

编译加载驱动

dmesg -c
insmod helloDev.ko
dmesg

测试驱动

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>

#define DATA_NUM    (64)
int main(int argc, char *argv[])
{
   int fd, i;
   int r_len, w_len;
   fd_set fdset;
   char buf[DATA_NUM]="hello world";
   memset(buf,0,DATA_NUM);
   fd = open("/dev/hello", O_RDWR);
printf("%d\r\n",fd);
   if(-1 == fd) {
      perror("open file error\r\n");
return -1;
   }
else {
printf("open successe\r\n");
}
   
   w_len = write(fd,buf, DATA_NUM);
   r_len = read(fd, buf, DATA_NUM);
   printf("%d %d\r\n", w_len, r_len);
   printf("%s\r\n",buf);

   return 0;
}

在运行测试程序之前,先手动在/dev创建设备文件:

mknod /dev/hello c 232 0

c:字符设备
232:主设备号
0:次设备号

然后可以运行测试程序进行测试。