驱动代码整理

news2024/11/18 19:32:48

一,控制LED灯控制实验

头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#define LED1_MODER   0X50006000
#define LED1_ODR     0X50006014
#define LED1_RCC     0X50000A28

#endif

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include"head.h"

unsigned int major;
struct class *cls;
struct device *dev;
unsigned int *vir_moder;
unsigned int *vir_odr;
unsigned int *vir_rcc;


//0.构建操作方法结构体并初始化
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof){
	return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t * lof){
	//4.控制灯的亮灭
	int ret = 0;
	ret = copy_from_user(kbuf, ubuf, size);
	if(ret){
		printk("copy from user err\n");
		return -EIO;
	}
	if('1' == kbuf[0]){
		(*vir_odr) |= (0x1 << 10);
	}else if('0' == kbuf[0]){
		(*vir_odr) &= (0x1 << 10);
	}
	
	return 0;
}
int mycdev_open(struct inode *inode, struct file *file){
	return 0;
}
int mycdev_close(struct inode *inode, struct file *file){
	return 0;
}
struct file_operations fops = {
	.read = mycdev_read,
	.wriet = mycdev_write,
	.open = mycdev_open,
	.release = mycdev_close,
};


static int __init mycdev_init(void)
{
	int i = 0;
	int ret = 0;
    //1.注册字符设备驱动,获取设备号
    major = register_chrdev(0, "mychrdev", &fops);
    if(major < 0){
		printk("register \n");
		return major;
    }
	printk("注册字符驱动成功,major = %d\n", major);
    //2.提交设备目录/提交设备节点信息
	cls = class_create(THIS_MODULE, "myclass");
	if(IS_ERR(cls)){
		printk("class create err\n");
		ret = -PTR_ERR(cls);	
		goto out1;
	}
	printk("class create success\n");
	for(i = 0; i < 3; i++){
		dev = device_create(cls, MKDEV(major, i), NULL, "myled%d",i);
		if(IS_ERR(dev)){
			printk("device create err\n");
			ret = -PTR_ERR(dev);
			goto out2;
		}
	}
	printk("device create success\n");
    //3.物理内存映射 / 硬件寄存器使能和初始化
	vir_rcc = ioremap(LED1_RCC, 4);
	if(NULL == vir_rcc){
		printk("ioremap err\n");
		goto out3;
	}
	vir_odr = ioremap(LED1_ODR, 4);
	if(NULL == vir_odr){
		printk("ioremap err\n");
		goto out4;
	}
	vir_moder = ioremap(LED1_MODER, 4);
	if(NULL == vir_moder){
		printk("ioremap err\n");
		goto out5;
	}
	printk("ioremap success\n");
	
	(*vir_rcc) |= (0x1 << 4);
	(*vir_moder) &= (~(0x3 << 20));
	(*vir_moder) |= (0x1 << 20);
	(*vir_odr) &= (~(0x1 << 10));
	
	return 0;
out5:
	iounmap(vir_odr);
out4:
	iounmap(vir_rcc);
out 3:
	i += 1; 
out2:
	for(--i; i >= 0; i--){
		device_destroy(cls, MKDEV(major, i));		
	}
	class_destroy(cls);
out1:
	return ret;
}

static void __exit mycdev_exit(void)
{
	int i = 0;
    //-1.取消物理内存映射
	iounmap(vir_moder);
	iounmap(vir_odr);
	iounmap(vir_rcc);
    //-2.注销设备节点信息
	for(i = 0; i < 3; i++){
		device_destroy(cls, MKDEV(major, i));		
	}
	class_destroy(cls);
    //-3.注销设备信息
	unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    int fd = open("/dev/mychrdev", O_RDWR);
    if (fd < 0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }
    while (1)
    {
        printf("请输入对LED1的控制命令 1(开灯)0(关灯)>");
        fgets(buf, sizeof(buf), stdin); // 在终端输入数据传递到buf
        buf[strlen(buf) - 1] = '\0';    // 替换末尾的'\n'
        write(fd, buf, sizeof(buf));
    }
    close(fd);

    return 0;
}

二,控制三盏灯实验

头文件

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28
#endif 

驱动

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

int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char  *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     unsigned long ret;
    //向用户空间读取拷贝
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{
    unsigned long ret;
    //从用户空间读取数据
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//关灯
                vir_led3->ODR &= (~(1<<8));
            else//开灯
                vir_led3->ODR |= 1<<8;
            break;
    }
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

//定义操作方法结构体变量并赋值
struct file_operations fops={

    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    //字符设备驱动注册
    major=register_chrdev(0,"mychrdev",&fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n",major);

    //寄存器映射以及初始化
    all_led_init();

    return 0;
}
static void __exit mycdev_exit(void)
{
    //取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

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


int main(int argc, char const *argv[])
{
    char buf[128]={0};
    int fd=open("/dev/mychrdev",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入两个字符\n");
        printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");
        printf("第二个字符:0(关灯) 1(开灯)\n");
        printf("请输入>");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        //向设备文件中写
        write(fd,buf,sizeof(buf));
    }
    
    close(fd);

    return 0;
}

三,自动创建设备节点实例

头文件

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28
#endif 

驱动

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include"head.h"

int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char  *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
     unsigned long ret;
    //向用户空间读取拷贝
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{
    unsigned long ret;
    //从用户空间读取数据
    if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)//拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//关灯
                vir_led3->ODR &= (~(1<<8));
            else//开灯
                vir_led3->ODR |= 1<<8;
            break;
    }
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

//定义操作方法结构体变量并赋值
struct file_operations fops={

    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //寄存器地址的映射
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\n",__LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    //寄存器的初始化
    //rcc
    (*vir_rcc) |= (0X3<<4);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_led3->MODER &= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR &= (~(1<<8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    //字符设备驱动注册
    major=register_chrdev(0,"mychrdev",&fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n",major);
    
    //寄存器映射以及初始化
    all_led_init();
    //向上提交目录
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    //向上提交设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mychrdev%d",i);
        if(IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");


    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录信息
    class_destroy(cls);
    //取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    
   
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

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


int main(int argc, char const *argv[])
{
    char buf[128]={0};
    int fd=open("/dev/mychrdev0",O_RDWR);
    if(fd<0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while(1)
    {
        //从终端读取
        printf("请输入两个字符\n");
        printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");
        printf("第二个字符:0(关灯) 1(开灯)\n");
        printf("请输入>");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        //向设备文件中写
        write(fd,buf,sizeof(buf));
    }
    close(fd);

    return 0;
}

四,ioctl函数实例(不加第三个参数)

头文件

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28

//构建LED开关的功能码,不添加ioctl第三个参数
#define  LED_ON _IO('l',1)
#define  LED_OFF _IO('l',0)
#endif 

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

    switch (cmd)
    {
    case LED_ON: // 开灯

        vir_led1->ODR |= 1 << 10;

        vir_led2->ODR |= 1 << 10;

        vir_led3->ODR |= 1 << 8;

        break;
    case LED_OFF: // 关灯

        vir_led1->ODR &= (~(1 << 10));

        vir_led2->ODR &= (~(1 << 10));

        vir_led3->ODR &= (~(1 << 8));

        break;
    }
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"

int main(int argc, char const *argv[])
{
    int a;
    char buf[128] = {0};
    int fd = open("/dev/mychrdev0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while (1)
    {
        // 从终端读取
        printf("请输入对LED灯的控制:1(开灯)0(关灯)>");
        scanf("%d", &a);
        switch (a)
        {
        case 1:
            ioctl(fd, LED_ON); // 开灯
            break;
        case 0:
            ioctl(fd, LED_OFF); // 关灯
        }
    }

    close(fd);

    return 0;
}

五,ioctl函数实例(第三个参数是指针)

头文件

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28

//构建LED开关的功能码,添加ioctl第三个参数int
#define LED_ON _IOW('l', 1, int *)
#define LED_OFF _IOW('l', 0, int *)
#endif 

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which;
    //获取arg对应的用户空间中到的值
    int ret=copy_from_user(&which,(void *)arg,4);
    if(ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }
    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR |= 1 << 10;
            break;
        case 2:
            vir_led2->ODR |= 1 << 10;
            break;
        case 3:
            vir_led3->ODR |= 1 << 8;
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case 2:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case 3:
            vir_led3->ODR &= (~(1 << 8));
            break;
        }
        break;
    }
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"

int main(int argc, char const *argv[])
{
    int a,b;
    char buf[128] = {0};
    int fd = open("/dev/mychrdev0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while (1)
    {
        // 从终端读取
        printf("请输入对LED灯的控制:1(开灯)0(关灯)>");
        scanf("%d", &a);
        printf("请输入要控制的灯:1(LED1) 2(LED2) 3(LED3)>");
        scanf("%d",&b);
        switch (a)
        {
        case 1:
            ioctl(fd, LED_ON,&b); // 开灯
            break;
        case 0:
            ioctl(fd, LED_OFF,&b); // 关灯
        }
    }

    close(fd);

    return 0;
}

六,字符设备驱动分步注册实例

驱动

#include <linux/init.h>
#include <linux/module.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
struct cdev *cdev;
char kbuf[128]={0};
unsigned int major=0;
unsigned int minor=0;
dev_t devno;
module_param(major,uint,0664);//方便在命令行传递major的值
struct class*cls;
struct device *dev;
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    
    int ret;
    //从用户拷贝
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user err\n");
        return -EIO;
    }
    
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
// 定义一个操作方法结构体对象并且初始化
struct file_operations fops = {
    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};
static int __init mycdev_init(void)
{
    int ret;
    //为字符设备驱动对象申请空间
    cdev=cdev_alloc();
    if(cdev==NULL)
    {
        printk("字符设备驱动对象申请空间失败\n");
        ret=-EFAULT;
        goto out1;
    }
    printk("申请对象空间成功\n");
    //初始化字符设备驱动对象
    cdev_init(cdev,&fops);
    //申请设备号
    if(major>0)//静态指定设备号
    {
        ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
        if(ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if(major==0)//动态申请设备号
    {
        ret=alloc_chrdev_region(&devno,minor,3,"myled");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//获取主设备号
        minor=MINOR(devno);//获取次设备号

    }
    printk("申请设备号成功\n");
    //注册字符设备驱动对象
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //向上提交目录信息
    cls=class_create(THIS_MODULE,"myled");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret=-PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    //向上提交设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
        if(IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret=-PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");
    return 0;
out5:
    //释放前一次提交成功的设备信息
    for(--i;i>=0;i--)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    class_destroy(cls);//释放目录
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),3);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    //释放节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录
    class_destroy(cls);
    //注销驱动对象
    cdev_del(cdev);
    //释放设备号
    unregister_chrdev_region(MKDEV(major,minor),3);
    //释放对象空间
    kfree(cdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

 七,设备文件和设备的绑定(部分)

int mycdev_open(struct inode *inode, struct file *file)
{
    int min=MINOR(inode->i_rdev);//根据打开的文件对应的设备号获取次设备号
    file->private_data=(void *)min;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
   int min=(int)file->private_data;
    switch (min){
        case 0://控制LED1
            switch(cmd){
                case LED_ON:
                    //开灯
                    break;
                case LED_OFF:
                    //关灯
                    break;                                
            }
            break;
       case 1://控制LED2
            break;
       case 2://控制LED3
 
            break;
    }
    return 0;
}

八,自旋锁实例

API

1.定义自旋锁 spinlock_t lock;

2.初始化自旋锁 spin_lock_init(&lock);

3.上锁(获取锁) void spin_lock(spinlock_t *lock)

4.解锁(释放锁) void spin_unlock(spinlock_t *lock)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
// 定义自旋锁
spinlock_t lock;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which;
    // 获取arg对应的用户空间中到的值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }
    //上锁
    spin_lock(&lock);
    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR |= 1 << 10;
            break;
        case 2:
            vir_led2->ODR |= 1 << 10;
            break;
        case 3:
            vir_led3->ODR |= 1 << 8;
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case 2:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case 3:
            vir_led3->ODR &= (~(1 << 8));
            break;
        }
        break;
    }
    //解锁
      spin_unlock(&lock);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    // 初始化自旋锁
    spin_lock_init(&lock);
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

九,信号量实例

1.定义一个信号量 struct semaphore sema;

2.初始化信号量 void sema_init(struct semaphore *sem, int val)

        参数: sem:信号量指针 val:给信号量的初始值

3.获取信号量(上锁) void down(struct semaphore *sem)//信号量数值-1

4.释放信号量(解锁) void up(struct semaphore *sem);

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
// 定义一个信号量
struct semaphore sema;
int mycdev_open(struct inode *inode, struct file *file)
{
    //上锁
    down(&sema);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which;
    // 获取arg对应的用户空间中到的值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }
   
    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR |= 1 << 10;
            break;
        case 2:
            vir_led2->ODR |= 1 << 10;
            break;
        case 3:
            vir_led3->ODR |= 1 << 8;
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case 2:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case 3:
            vir_led3->ODR &= (~(1 << 8));
            break;
        }
        break;
    }
   
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    //解锁
    up(&sema);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    // 初始化信号量
    sema_init(&sema,1);
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

九,互斥体实例

1.定义互斥体 struct mutex mutex;

2.初始化互斥体 mutex_init(&mutex);

3.上锁 void mutex_lock(struct mutex *lock)

4.解锁 void mutex_unlock(struct mutex *lock)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
// 定义互斥体
struct mutex mutex;
int mycdev_open(struct inode *inode, struct file *file)
{
    // 上锁
    mutex_lock(&mutex);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int which;
    // 获取arg对应的用户空间中到的值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }

    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR |= 1 << 10;
            break;
        case 2:
            vir_led2->ODR |= 1 << 10;
            break;
        case 3:
            vir_led3->ODR |= 1 << 8;
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case 2:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case 3:
            vir_led3->ODR &= (~(1 << 8));
            break;
        }
        break;
    }

    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    // 解锁
    mutex_unlock(&mutex);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
    // 初始化互斥体
    mutex_init(&mutex);
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定

头文件

#ifndef __MYLED_H__
#define __MYLED_H__
typedef struct{
volatile unsigned int MODER;
volatile unsigned int OTYPER;
volatile unsigned int OSPEEDR;
volatile unsigned int PUPDR;
volatile unsigned int IDR;
volatile unsigned int ODR;
volatile unsigned int BSRR;
}gpio_t;
#define PHY_RCC_ADDR 0x50000a28
#define PHY_LED1_ADDR 0x50006000
#define PHY_LED2_ADDR 0x50007000
#define PHY_LED3_ADDR 0x50006000
enum{
LED1,
LED2,
LED3
};
#endif

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include "myled.h"
#define CNAME "myled"
#define LED1_ON (virt_led1->ODR |= (1 << 10))
#define LED1_OFF (virt_led1->ODR &= ~(1 << 10))
#define LED2_ON (virt_led2->ODR |= (1 << 10))
#define LED2_OFF (virt_led2->ODR &= ~(1 << 10))
#define LED3_ON (virt_led3->ODR |= (1 << 8))
#define LED3_OFF (virt_led3->ODR &= ~(1 << 8))
int major;
char kbuf[2] = {0};
gpio_t *virt_led1;
gpio_t *virt_led2;
gpio_t *virt_led3;
unsigned int *virt_rcc;
struct class *cls;
struct device *dev;
int myled_open(struct inode *inode, struct file *file)
{
	int curno;
	curno = MINOR(inode->i_rdev);
	file->private_data = (void *)curno;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}
ssize_t myled_read(struct file *file,
		char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_to_user(ubuf, kbuf, size);
	if (ret)
	{
		printk("copy data to user error\n");
		return -EIO;
	}
	return size;
}
ssize_t myled_write(struct file *file,
		const char __user *ubuf, size_t size, loff_t *off)
{
	int ret;
	int curno = (int)file->private_data;//获取次设备号
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	if (size > sizeof(kbuf))
		size = sizeof(kbuf);
	ret = copy_from_user(kbuf, ubuf, size);
	if (ret)
	{
		printk("copy data from user error\n");
		return -EIO;
	}
	switch (curno)//通过次设备号来判断是控制哪一个灯
	{
	case LED1:
		kbuf[0] == '1' ? LED1_ON : LED1_OFF;
		break;
	case LED2:
		kbuf[0] == '1' ? LED2_ON : LED2_OFF;
		break;
	case LED3:
		kbuf[0] == '1' ? LED3_ON : LED3_OFF;
		break;
	default:
		printk("input arg error,try again\n");
		return -EINVAL;
	}
	return size;
}
int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
	return 0;
}
const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.release = myled_close,
};
int all_led_init(void)
{
	virt_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
	if (virt_led1 == NULL)
	{
		printk("ioremap led1 addr error\n");
		return -ENOMEM;
	}
	virt_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
	if (virt_led2 == NULL)
	{
		printk("ioremap led2 addr error\n");
		return -ENOMEM;
	}
	virt_led3 = virt_led1;
	virt_rcc = ioremap(PHY_RCC_ADDR, 4);
	if (virt_rcc == NULL)
	{
		printk("ioremap rcc addr error\n");
		return -ENOMEM;
	}
	*virt_rcc |= (3 << 4); // rcc gpioe gpiof enable
	// init led1
	virt_led1->MODER &= ~(3 << 20);
	virt_led1->MODER |= (1 << 20); // output
	virt_led1->ODR &= ~(1 << 10); // led1 off
	// init led2
	virt_led2->MODER &= ~(3 << 20);
	virt_led2->MODER |= (1 << 20); // output
	virt_led2->ODR &= ~(1 << 10); // led2 off
	// init led3
	virt_led3->MODER &= ~(3 << 16);
	virt_led3->MODER |= (1 << 16); // output
	virt_led3->ODR &= ~(1 << 8); // led3 off
	return 0;
}
static int __init myled_init(void)
{
	int i;
	// 1.注册字符设备驱动
	major = register_chrdev(0, CNAME, &fops);
	if (major < 0)
	{
		printk("register char device driver error\n");
		return major;
	}
	printk("register myled driver success... major = %d\n", major);
	// 2.led地址映射及初始化
	all_led_init();
	// 3.自动创建设备节点
	cls = class_create(THIS_MODULE, CNAME);
	if (IS_ERR(cls))
	{
		printk("class create error\n");
		return PTR_ERR(cls);
	}
	for (i = 0; i < 3; i++)
	{
		dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d",
				i);
		if (IS_ERR(dev))
		{
			printk("device create error\n");
			return PTR_ERR(dev);
		}
	}
	return 0;
}
static void __exit myled_exit(void)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		device_destroy(cls, MKDEV(major, i));
	}
	class_destroy(cls);
	iounmap(virt_rcc);
	iounmap(virt_led1);
	iounmap(virt_led2);
	unregister_chrdev(major, CNAME);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

十一,原子操作

1.定义原子变量并且初始化 atomic_t atm=ATOMIC_INIT(1);//将原子变量的数值初始化为1 2.int atomic_dec_and_test(atomic_t *atm)

功能:将原子变量的数值-1并且和0比较

        参数: atm:原子变量的指针 返回值:如果原子变量-1后结果为0,则返回真,否则返回假

3.void atomic_inc(atomic_t *atm)

        功能:原子变量的数值+1

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"

int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
// 定义原子变量并且初始化
atomic_t atm=ATOMIC_INIT(1);//将原子变量的数值初始化为1
int mycdev_open(struct inode *inode, struct file *file)
{
    
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    //上锁
    if(!atomic_dec_and_test(&atm))
    {
        atomic_inc(&atm);//将没访问临界资源的进程减去的1加回来,防止类似于死锁现象的发生
        return -1;
    }
    int which;
    // 获取arg对应的用户空间中到的值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }

    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR |= 1 << 10;
            break;
        case 2:
            vir_led2->ODR |= 1 << 10;
            break;
        case 3:
            vir_led3->ODR |= 1 << 8;
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case 2:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case 3:
            vir_led3->ODR &= (~(1 << 8));
            break;
        }
        break;
    }
    //解锁
    atomic_inc(&atm);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

int all_led_init(void)
{
    // 寄存器地址的映射
    vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));
    if (vir_led1 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));
    if (vir_led2 == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    vir_led3 = vir_led1;
    vir_rcc = ioremap(PHY_RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("ioremap filed:%d\n", __LINE__);
        return -ENOMEM;
    }
    printk("物理地址映射成功\n");
    // 寄存器的初始化
    // rcc
    (*vir_rcc) |= (0X3 << 4);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_led3->MODER &= (~(3 << 16));
    vir_led1->MODER |= (1 << 16);
    vir_led1->ODR &= (~(1 << 8));
    printk("寄存器初始化成功\n");

    return 0;
}

static int __init mycdev_init(void)
{
   
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 寄存器映射以及初始化
    all_led_init();
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");

    return 0;
}
static void __exit mycdev_exit(void)
{
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);
    // 取消地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十二,阻塞IO

1.定义一个等待队列头 wait_queue_head_t wq_head;

2.初始化等待队列 init_waitqueue_head(&wq_head);

3.将进程切换为不可中断的休眠态wait_event(wq_head, condition) 

        3.2将进程切换为可中断的休眠态wait_event_interruptible

4.唤醒不可中断休眠态的进程 wake_up(&wq_head)

        4.2唤醒可中断休眠态的进程 wake_up_interruptible(&wq_head)

进程1(阻塞等待数据)

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


int main(int argc, char const *argv[])
{
    
    char buf[128] = {0};
    int fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while (1)
    {
        read(fd,buf,sizeof(buf));
        printf("%s\n",buf);
    }

    close(fd);

    return 0;
}

进程2(模拟硬件数据到达)

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


int main(int argc, char const *argv[])
{
  
    char buf[128] = "hello world";
    int fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    write(fd,buf,sizeof(buf));
    
    close(fd);

    return 0;
}

驱动

#include <linux/init.h>
#include <linux/module.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/uaccess.h>
#include<linux/slab.h>
#include<linux/wait.h>
struct cdev *cdev;
char kbuf[128]={0};
unsigned int major=0;
unsigned int minor=0;
dev_t devno;
module_param(major,uint,0664);//方便在命令行传递major的值
struct class*cls;
struct device *dev;
unsigned int condition=0;
//定义一个等待队列头
wait_queue_head_t wq_head;
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    //判断IO方式
    if(file->f_flags&O_NONBLOCK)//非阻塞
    {}
    else//阻塞
    {
        wait_event_interruptible(wq_head,condition);//先检查condition再将进程休眠 
    }
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    condition=0;//下一次硬件数据没有就绪
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    
    int ret;
    //从用户拷贝数据,模拟硬件数据
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user err\n");
        return -EIO;
    }
    condition=1;//表示硬件数据就绪
    wake_up_interruptible(&wq_head);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
// 定义一个操作方法结构体对象并且初始化
struct file_operations fops = {
    .open=mycdev_open,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};
static int __init mycdev_init(void)
{
    //初始化等待队列
init_waitqueue_head(&wq_head);
    int ret;
    //为字符设备驱动对象申请空间
    cdev=cdev_alloc();
    if(cdev==NULL)
    {
        printk("字符设备驱动对象申请空间失败\n");
        ret=-EFAULT;
        goto out1;
    }
    printk("申请对象空间成功\n");
    //初始化字符设备驱动对象
    cdev_init(cdev,&fops);
    //申请设备号
    if(major>0)//静态指定设备号
    {
        ret=register_chrdev_region(MKDEV(major,minor),3,"myled");
        if(ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if(major==0)//动态申请设备号
    {
        ret=alloc_chrdev_region(&devno,minor,3,"myled");
        if(ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major=MAJOR(devno);//获取主设备号
        minor=MINOR(devno);//获取次设备号

    }
    printk("申请设备号成功\n");
    //注册字符设备驱动对象
    ret=cdev_add(cdev,MKDEV(major,minor),3);
    if(ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //向上提交目录信息
    cls=class_create(THIS_MODULE,"myled");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret=-PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    //向上提交设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
        if(IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret=-PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");
    return 0;
out5:
    //释放前一次提交成功的设备信息
    for(--i;i>=0;i--)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    class_destroy(cls);//释放目录
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major,minor),3);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    //释放节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录
    class_destroy(cls);
    //注销驱动对象
    cdev_del(cdev);
    //释放设备号
    unregister_chrdev_region(MKDEV(major,minor),3);
    //释放对象空间
    kfree(cdev);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十三,IO多路复用

应用程序(IO多路复用)

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>

int main(int argc, char const *argv[])
{

    char buf[128] = {0};
    int fd1, fd2;
    // 打开设备文件
    fd1 = open("/dev/input/mouse0", O_RDWR);
    if (fd1 < 0)
    {
        printf("打开鼠标设备文件失败\n");
        exit(-1);
    }
    fd2 = open("/dev/myled0", O_RDWR);
    if (fd2 < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    // 定义事件集合
    fd_set readfds;
    while (1)
    {
        // 清空事件集合
        FD_ZERO(&readfds);
        // 将要监听的文件描述符添加到可读集合中
        FD_SET(fd1, &readfds);
        FD_SET(fd2, &readfds);
        // 等待事件就绪
        int ret = select(fd2 + 1, &readfds, NULL, NULL, NULL);
        if (ret < 0)
        {
            printf("select err\n");
            exit(-1);
        }
        // 判断事件是否发生
        // 没发生的事件会被清除出可读集合,所以只需要判断文件描述符是否还在集合中就可以知道事件是否发生
        if (FD_ISSET(fd1, &readfds))
        {
            read(fd1, buf, sizeof(buf));
            printf("鼠标事件发生%s\n", buf);
        }
        if (FD_ISSET(fd2, &readfds))
        {
            read(fd2, buf, sizeof(buf));
            printf("自定义设备事件发生%s\n", buf);
        }
    }
    close(fd1);
    close(fd2);

    return 0;
}

应用程序(模拟自定义设备数据就绪)

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


int main(int argc, char const *argv[])
{
  
    char buf[128] = "hello world";
    int fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    write(fd,buf,sizeof(buf));
    
    close(fd);

    return 0;
}

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include<linux/poll.h>
struct cdev *cdev;
char kbuf[128] = {0};
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); // 方便在命令行传递major的值
struct class *cls;
struct device *dev;
unsigned int condition = 0;
// 定义一个等待队列头
wait_queue_head_t wq_head;
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    condition = 0; // 下一次硬件数据没有就绪
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{

    int ret;
    // 从用户拷贝数据,模拟硬件数据
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret){
        printk("copy_from_user err\n");
        return -EIO;
    }
    
    condition = 1; // 表示硬件数据就绪
    wake_up_interruptible(&wq_head);
    return 0;
}
// 封装POLL方法
__poll_t mycdev_poll(struct file *file, struct poll_table_struct *wait)
{
    __poll_t mask=0;
    // 向上提交等待队列头
    poll_wait(file,&wq_head,wait);
    //根据事件是否发生给一个合适的返回值
    if(condition)
    {
        mask=POLLIN;
    }
    return mask;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义一个操作方法结构体对象并且初始化
struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .poll=mycdev_poll,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{
    // 初始化等待队列
    init_waitqueue_head(&wq_head);
    int ret;
    // 为字符设备驱动对象申请空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("字符设备驱动对象申请空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("申请对象空间成功\n");
    // 初始化字符设备驱动对象
    cdev_init(cdev, &fops);
    // 申请设备号
    if (major > 0) // 静态指定设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if (major == 0) // 动态申请设备号
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "myled");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno); // 获取主设备号
        minor = MINOR(devno); // 获取次设备号
    }
    printk("申请设备号成功\n");
    // 注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    // 向上提交目录信息
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");
    return 0;
out5:
    // 释放前一次提交成功的设备信息
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls); // 释放目录
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    // 释放节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录
    class_destroy(cls);
    // 注销驱动对象
    cdev_del(cdev);
    // 释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    // 释放对象空间
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十四,epoll实例

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/epoll.h>
/* According to earlier standards */
#include <sys/time.h>

int main(int argc, char const *argv[])
{
    int fd1, fd2, epfd;
    struct epoll_event event;      // 用于操作epoll
    struct epoll_event events[10]; // 存放就绪事件描述符的数组
    char buf[128] = {0};
    // 创建epoll句柄
    epfd = epoll_create(1);
    if (epfd < 0)
    {
        printf("epoll_create filed\n");
        exit(-1);
    }
    // 打开设备文件
    fd1 = open("/dev/input/mouse0", O_RDWR);
    if (fd1 < 0)
    {
        printf("打开鼠标设备文件失败\n");
        exit(-1);
    }
    fd2 = open("/dev/mycdev0", O_RDWR);
    if (fd2 < 0)
    {
        printf("打开鼠标设备文件失败\n");
        exit(-1);
    }
    // 添加准备就绪事件进入epoll;
    event.events = EPOLLIN; // 读事件
    event.data.fd = fd1;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &event) < 0)
    {
        printf("epoll_ctl add filed\n");
    }
    event.events = EPOLLIN; // 读事件
    event.data.fd = fd2;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &event) < 0)
    {
        printf("epoll_ctl add filed\n");
    }

    // 监听事件是否发生
    while (1)
    {
        // 如果成功,ret接收返回的事件个数,把就绪的事件放在events数组中
        int ret = epoll_wait(epfd, events, 10, -1);
        if (ret < 0)
        {
            printf("epoll_wait filed\n");
            exit(-1);
        }
        int i;
        // 循环遍历数组,做事件的处理
        for (i = 0; i < ret; i++)
        {
            if (events[i].events & EPOLLIN)//判断发生的事件是不是读事件
            {
                read(events[i].data.fd, buf, sizeof(buf));
                printf("buf:%s\n", buf);
            }
        }
    }
    close(fd1);
    close(fd2);
    return 0;
}

十五,信号驱动IO

应用程序(信号驱动IO)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/epoll.h>
/* According to earlier standards */
#include <sys/time.h>
int fd; // 存放就绪事件描述符的数组
char buf[128] = {0};
// 定义信号处理函数
void sigio_handler(int sig)
{
    read(fd, buf, sizeof(buf));
    printf("buf:%s\n", buf);
}
int main(int argc, char const *argv[])
{

    // 打开设备文件
    fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开鼠标设备文件失败\n");
        exit(-1);
    }
    // 注册SIGIO信号的信号处理函数
    signal(SIGIO, sigio_handler);
    // 回调驱动中的fasync方法,完成发送信号之前的准备工作
    int flags = fcntl(fd,F_GETFL);     // 获取文件描述符属性
    fcntl(fd,F_SETFL,flags|FASYNC); // 在文件描述符表的flags中添加FASYNC,就可以回调fasync方法
    fcntl(fd,F_SETOWN,getpid());//驱动发送信号只发送给当前进程
    while(1)
    {
        printf("aaaaa\n");
        sleep(1);
    }
        close(fd);

    return 0;
}

应用程序(模拟硬件数据到达)

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


int main(int argc, char const *argv[])
{

    char buf[128] = "hello world";
    int fd = open("/dev/myled0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    write(fd, buf, sizeof(buf));

    close(fd);

    return 0;
}

驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/poll.h>
struct cdev *cdev;
char kbuf[128] = {0};
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); // 方便在命令行传递major的值
struct class *cls;
struct device *dev;
struct fasync_struct *fp; // 定义一个异步对象指针

// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    // 判断IO方式
    if (file->f_flags & O_NONBLOCK) // 非阻塞
    {
    }
    else // 阻塞
    {
    }
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }

    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{

    int ret;
    // 从用户拷贝数据,模拟硬件数据
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy_from_user err\n");
        return -EIO;
    }
    //发送信号
    kill_fasync(&fp,SIGIO,POLL_IN);
    return 0;
}

int mycdev_fasync(int fd, struct file *file, int on) // 异步操作方法
{
    // 完成发送信号之前的准备工作
    fasync_helper(fd, file, on, &fp);
    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义一个操作方法结构体对象并且初始化
struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .fasync = mycdev_fasync,
    .release = mycdev_close,
};
static int __init mycdev_init(void)
{

    int ret;
    // 为字符设备驱动对象申请空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("字符设备驱动对象申请空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("申请对象空间成功\n");
    // 初始化字符设备驱动对象
    cdev_init(cdev, &fops);
    // 申请设备号
    if (major > 0) // 静态指定设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto out2;
        }
    }
    else if (major == 0) // 动态申请设备号
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "myled");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno); // 获取主设备号
        minor = MINOR(devno); // 获取次设备号
    }
    printk("申请设备号成功\n");
    // 注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    // 向上提交目录信息
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息成功\n");
    return 0;
out5:
    // 释放前一次提交成功的设备信息
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls); // 释放目录
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
    kfree(cdev);
out1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    // 释放节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录
    class_destroy(cls);
    // 注销驱动对象
    cdev_del(cdev);
    // 释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    // 释放对象空间
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十六,epoll并发服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>

#define BUFLEN 128
int main(int argc,char ** argv){
       
    int serverFd,clientFd;
    int len,ret,rlen;
    char buf[BUFLEN];
    struct sockaddr_in serverAddr,clientAddr;
    len = sizeof(serverAddr);
    serverFd = socket(AF_INET,SOCK_STREAM,0);
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(55555);
    serverAddr.sin_addr.s_addr = inet_addr("192.168.3.120");
    
    ret = bind(serverFd,(struct sockaddr *)&serverAddr,len);
    if(ret<0){
        perror("Failed to bind");
        return -1;
    }
    ret = listen(serverFd,10);
    if(ret<0){
        perror("Failed to bind");
        return -1;
    }

    int epfd,epct,i;
    struct epoll_event event;       //定义epoll 事件
    struct epoll_event events[20];  //定义epoll 事件集合
    epfd = epoll_create(1); // 创建epoll 的fd //红黑树根节点

    event.data.fd = serverFd;           //填充事件的fd
    event.events = EPOLLIN | EPOLLET;   //填充 事件类型 
    epoll_ctl(epfd,EPOLL_CTL_ADD,serverFd,&event);  //把serverFd(监听FD)注册到epfd中
    
    
    while(1){
        
        epct = epoll_wait(epfd,events,20,-1); // 等待事件到来,阻塞模式 
        for(i=0;i<epct;i++){  //根据epoll返回的值来查询事件
            
            if(events[i].data.fd == serverFd){ // 如果事件的fd是监听fd,调用accept处理
                clientFd = accept(events[i].data.fd,(struct sockaddr *)&clientAddr,&len);
                event.data.fd = clientFd;
                event.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,clientFd,&event);
                
                
            }else {   //如果不是serverFd,应是client数据事件,调用读数据
                
                memset(buf,0,BUFLEN);
                rlen = read(events[i].data.fd,buf,BUFLEN);
                if(rlen <=0){ //客户端断开 
                    printf("connect disconnected\n");
                    close(events[i].data.fd); //关闭连接
                    epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,&event); //删除监听的客户端fd
                    continue;
                }
                printf("fd:%d data:%s\n",events[i].data.fd,buf);    
            }    
        }
    }
}

十七,GPIO子系统使用实例(新版API)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
/*myled{
    led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚  0,表示gpio默认属性
    led2-gpio=<&gpiof 10 0>;
    led3-gpio=<&gpioe 8 0>;*/
    struct device_node *dnode;
 struct gpio_desc *gpiono; 
static int __init mycdev_init(void)
{
    //解析LED的设备树节点
    dnode=of_find_node_by_path("/myled");
    if(dnode==NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析GPIO信息成功\n");
    //申请gpio对象
    gpiono=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpiono))
    {
        printk("申请gpio对象失败\n");
        return -ENXIO;
    }
    printk("申请gpio信息对象成功\n");
    
    //亮灯
    gpiod_set_value(gpiono,1);

    return 0;
}
static void __exit mycdev_exit(void)
{
    //灭灯
    gpiod_set_value(gpiono,0);
    //释放gpio编号
    gpiod_put(gpiono);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十八,GPIO子系统实例(老版API)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
/*myled{
    led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚  0,表示gpio默认属性
    led2-gpio=<&gpiof 10 0>;
    led3-gpio=<&gpioe 8 0>;*/
struct device_node *dnode;//指向解析成功的设备树节点信息
unsigned int gpiono; //保存解析成功的GPIO编号
static int __init mycdev_init(void)
{
    //解析LED的设备树节点
    dnode=of_find_node_by_path("/myled");
    if(dnode==NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析GPIO信息成功\n");
    //获取GPIO编号
    gpiono=of_get_named_gpio(dnode,"led1-gpio",0);
    if(gpiono<0)
    {
        printk("GPIO编号解析失败\n");
    }
    printk("gpio编号解析成功%d\n",gpiono);
    //申请gpio编号
    int ret=gpio_request(gpiono,NULL);
    if(ret)
    {
        printk("申请gpio编号失败\n");
        return -1;
    }
    printk("申请gpio编号成功\n");
    //设置gpio管脚为输出,默认输出低电平
    gpio_direction_output(gpiono,0);
    //亮灯
    gpio_set_value(gpiono,1);

    return 0;
}
static void __exit mycdev_exit(void)
{
    //灭灯
    gpio_set_value(gpiono,0);
    //释放gpio编号
    gpio_free(gpiono);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十九,定时器使用实例

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/gpio.h>
    #include<linux/timer.h>
    /*myled{
        led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚  0,表示gpio默认属性
        led2-gpio=<&gpiof 10 0>;
        led3-gpio=<&gpioe 8 0>;*/
        struct device_node *dnode;
    struct gpio_desc *gpiono; 
    //分配定时器对象
struct timer_list mytimer;
//设置一个定时器处理函数
void mytimer_func(struct timer_list *timer)
{
     //LED1一秒亮一秒灭
        gpiod_set_value(gpiono,!gpiod_get_value(gpiono));
        mod_timer(timer,jiffies+HZ);
}
    static int __init mycdev_init(void)
    {
        //解析LED的设备树节点
        dnode=of_find_node_by_path("/myled");
        if(dnode==NULL)
        {
            printk("解析设备树节点失败\n");
            return -ENXIO;
        }
        printk("解析GPIO信息成功\n");
        //申请gpio对象
        gpiono=gpiod_get_from_of_node(dnode,"led1-gpio",0,GPIOD_OUT_LOW,NULL);
        if(IS_ERR(gpiono))
        {
            printk("申请gpio对象失败\n");
            return -ENXIO;
        }
        printk("申请gpio信息对象成功\n");
        //初始化定时器对象
        timer_setup(&mytimer,mytimer_func,0);
        mytimer.expires=jiffies+HZ;//设置定时一秒
        //注册定时器
        add_timer(&mytimer);
        
       

        return 0;
    }
    static void __exit mycdev_exit(void)
    {
        //注销定时器
        del_timer(&mytimer);
        //灭灯
        gpiod_set_value(gpiono,0);
        //释放gpio编号
        gpiod_put(gpiono);

    }
    module_init(mycdev_init);
    module_exit(mycdev_exit);
    MODULE_LICENSE("GPL");
    

十七,基于gpio子系统编写LED灯的驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include "head.h"

int major;
char kbuf[128] = {0};
 /*myled{
        led1-gpio=<&gpioe 10 0>;//10表示使用的gpioe第几个管脚  0,表示gpio默认属性
        led2-gpio=<&gpiof 10 0>;
        led3-gpio=<&gpioe 8 0>;*/
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
struct device_node *dnode;
struct gpio_desc *gpiono[3];

int mycdev_open(struct inode *inode, struct file *file)
{

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

    int which;
    // 获取arg对应的用户空间中到的值
    int ret = copy_from_user(&which, (void *)arg, 4);
    if (ret)
    {
        printk("从用户空间获取数据失败\n");
        return -EIO;
    }

    switch (cmd)
    {
    case LED_ON: // 开灯
        switch (which)
        {
        case 1: // LED1
            gpiod_set_value(gpiono[0],1);
            break;
        case 2:
            gpiod_set_value(gpiono[1],1);
            break;
        case 3:
            gpiod_set_value(gpiono[2],1);
            break;
        }
        break;
    case LED_OFF: // 关灯
        switch (which)
        {
        case 1: // LED1
            gpiod_set_value(gpiono[0],1);
            break;
        case 2:
           gpiod_set_value(gpiono[1],1);
            break;
        case 3:
            gpiod_set_value(gpiono[2],1);
            break;
        }
        break;
    }

    return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

// 定义操作方法结构体变量并赋值
struct file_operations fops = {

    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{

    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);

    // 向上提交目录
    cls = class_create(THIS_MODULE, "mychrdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mychrdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点信息成功\n");
    // 解析LED的设备树节点
    dnode = of_find_node_by_path("/myled");
    if (dnode == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析GPIO信息成功\n");
    // 申请gpio对象
    gpiono[0] = gpiod_get_from_of_node(dnode, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {   
        printk("申请gpio对象失败\n");
        return -ENXIO;
    }
     gpiono[1] = gpiod_get_from_of_node(dnode, "led2-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {
        printk("申请gpio对象失败\n");
        return -ENXIO;
    }
     gpiono[2] = gpiod_get_from_of_node(dnode, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono))
    {
        printk("申请gpio对象失败\n");
        return -ENXIO;
    }
    printk("申请gpio信息对象成功\n");
    return 0;
}
static void __exit mycdev_exit(void)
{
    //释放GPIO信息
    int i;
    for(i=0;i<3;i++)
    {
        gpiod_put(gpiono[i]);
    }
    /*销毁设备节点信息*/
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录信息
    class_destroy(cls);

    // 注销字符设备驱动
    unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

十八,按键1 中断注册实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
/*   myirq{
       compatible="hqyj,myirq";
       interrupt-parent=<&gpiof>; 
       interrupts=<9 0>,<7 0>,<8 0>;  
   };*/
unsigned int irqno;
struct device_node *dnode;
//定义中断处理函数 
irqreturn_t key_handler(int irq, void *dev)
{
    printk("KEY1_INTERRUPT\n");
    return IRQ_HANDLED;
}

static int __init mycdev_init(void)
{
    //解析按键的设备树节点
    dnode=of_find_compatible_node(NULL,NULL,"hqyj,myirq");
    if(dnode==NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析设备树节点成功\n");
    //解析按键的软中断号
    irqno=irq_of_parse_and_map(dnode,0);
    if(!irqno)
    {
        printk("解析按键1软中断号失败\n");
        return -ENXIO;
    }
    printk("解析按键1软中断号成功%d\n",irqno);
    //注册 按键1中断
    int ret=request_irq(irqno,key_handler,IRQF_TRIGGER_FALLING,"key_int",NULL);
    if(ret<0)
    {
        printk("注册按键1中断失败\n");
        return ret;
    }
    printk("注册按键1中断成功\n");
    return 0;
}
static void __exit mycdev_exit(void)
{
    //注销中断
    free_irq(irqno,NULL);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

一,中断——三个按键中断

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

/*
1.解析中断相关的设备树节点
    struct device_node *of_find_compatible_node( struct device_node *from, const char *type, const char *compat)
2.解析设备中断的软中断号
    unsigned int irq_of_parse_and_map(struct device_node *node, int index)
3.注册中断
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
    enum irqreturn {
        IRQ_NONE        = (0 << 0),//这个中断不是被这个设备触发,没被处理
        IRQ_HANDLED     = (1 << 0),//中断被正常处理 
        IRQ_WAKE_THREAD = (1 << 1),//唤醒一个线程处理中断
    };
4.注销中断
    void *free_irq(unsigned int irq, void *dev_id)
*/

struct device_node *dnode; //设备树节点
unsigned int irqno[3];//软中断号
//3.1中断处理函数
irqreturn_t key_handler(int arg, void *dev){
    int witch = (int)dev;
    switch(witch){
        case 0:
            printk("KEY1_INTERRUPT\n");
            break;
        case 1:
            printk("KEY2_INTERRUPT\n");
            break;
        case 2:
            printk("KEY3_INTERRUPT\n");
            break;
    }
    return IRQ_HANDLED;
}

static int __init mycdev_init(void)
{
    int i = 0;
    int ret = 0;
    //1,根据厂商信息解析按键的设备树节点
    dnode = of_find_compatible_node(NULL, NULL, "hqyj,myirq");
    if(NULL == dnode){
        printk("find device node err\n");
        return -ENXIO;
    }
    printk("find device node success\n");
    
    for(i = 0; i < 3; i++){
        //2,解析按键的软中断号
        irqno[i] = irq_of_parse_and_map(dnode, i);
        if(!irqno[i]){
            printk("parse and map irqno err\n");
            return -ENXIO;
        }
        printk("parse and map irqno success, irqno = %d\n", irqno[i]);
        //3,注册按键中断
        ret = request_irq(irqno[i], key_handler, IRQF_TRIGGER_FALLING, "myirq", (void *)i);
        if(ret){
            printk("request irq err\n");
            return ret;
        }
        printk("request irq success\n");
    }
     
    return 0;
}
static void __exit mycdev_exit(void)
{
    //4,注销中断
    int i = 0;
    for(i = 0; i < 3; i++){
        free_irq(irqno[i], (void *)i);
    }
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

一,中断底半部——tasklet

/*
1.解析中断相关的设备树节点
    struct device_node *of_find_compatible_node( struct device_node *from, const char *type, const char *compat)
2.解析设备中断的软中断号
    unsigned int irq_of_parse_and_map(struct device_node *node, int index)
3.注册中断
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
4,注销中断
    void *free_irq(unsigned int irq, void *dev_id)
   

1,初始化taklet对象 (二选一)
    void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data) 
    void tasklet_setup(struct tasklet_struct *t, void (*callback)(struct tasklet_struct *))
2,开启底半部
    void tasklet_schedule(struct tasklet_struct *t)

*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
/*   myirq{
       compatible="hqyj,myirq";
       interrupt-parent=<&gpiof>; 
       interrupts=<9 0>,<7 0>,<8 0>;  
   };*/

struct device_node *dnode; //设备树节点
unsigned int irqno; //软中断号
struct tasklet_struct tasklet;//分配tasklet对象

//tasklet1.1,定义底半部处理函数
void callback(struct tasklet_struct *tasklet){
	int i = 0;
	for(i = 1; i <= 100; i++){
		printk("callback %d\n",i);
	}
}

//中断3.1,定义中断处理函数
irqreturn_t key_handler(int arg, void *dev){
	printk("This is key_handler function\n");

	//tasklet2,开启底半部
    tasklet_schedule(&tasklet);

	return IRQ_HANDLED;
}

static int __init mycdev_init(void)
{
    int ret = 0;
    //中断1,解析按键的设备树节点
    dnode = of_find_node_by_name(NULL, "myirq");
    if(NULL == dnode){
        printk("find node err\n");
        return -ENXIO;
    }
    printk("find node success\n");

    //中断2,获取软中断号
    irqno = irq_of_parse_and_map(dnode, 0);
    if(!irqno){
        printk("get irqno error\n");
        return -ENXIO;
    }
    printk("get irqno success, irqno = %d\n", irqno);

    //中断3,注册中断到内核 
    ret = request_irq(irqno, key_handler, IRQF_TRIGGER_FALLING, "myirq", NULL);
	if(ret){
		printk("request err\n");
		return ret;
	}
	printk("request succsee\n");
    
    //tasklet1,初始化taskle对象
    tasklet_setup(&tasklet, callback);

    return 0;
}
static void __exit mycdev_exit(void)
{
    //中断4,注销中断
	free_irq(irqno, NULL);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

二十,platform总线实例

设备端

 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
// 封装release函数用于释放资源
void pdev_release(struct device *dev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
}
// 定义资源结构体数组并且初始化资源信息
struct resource res[] = {
    [0] = {
        .start = 0X12345678,
        .end = 0X12345678 + 59,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 71,
        .end = 71,
        .flags = IORESOURCE_IRQ,
    },
};
// 分配设备信息对象并初始化
struct platform_device pdev = {
    .name = "aaaaa",           // 名字
    .id = PLATFORM_DEVID_AUTO, // 总线编号
    .dev = {
        // 父类对象
        .release = pdev_release,
    },
    .resource = res,                  // 资源数组首地址
    .num_resources = ARRAY_SIZE(res), // 资源的个数
};
static int __init mycdev_init(void)
{
    // 注册对象
    platform_device_register(&pdev);
    return 0;
}
static void __exit mycdev_exit(void)
{
    // 注销对象
    platform_device_unregister(&pdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

驱动端代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    //注册驱动
    //创建设备文件
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    //销毁设备文件
    //注销驱动
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaaaa",
    },
};
static int __init mycdev_init(void)
{
    // 注册
    platform_driver_register(&pdrv);
    return 0;
}
static void __exit mycdev_exit(void)
{

    // 注销
    platform_driver_unregister(&pdrv);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

一键中断宏实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaaaa",
    },
};
//一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

二十一,驱动端获取设备信息实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
struct resource *res;
unsigned int irqno;
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    //获取MEM类型的资源  
    res=platform_get_resource(pdev,IORESOURCE_MEM,0);
    if(res==NULL)
    {
        printk("获取MEM类型资源失败\n");
        return -ENXIO;
    }
    //获取中断类型的资源
    irqno=platform_get_irq(pdev,0);
    if(irqno<0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }
    printk("mem资源%llx\n",res->start);
    printk("irq资源%d\n",irqno);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaaaa",
    },
};
//一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

二十一,id_table 名字表匹配实例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include<linux/mod_devicetable.h>
struct resource *res;
unsigned int irqno;
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    //获取MEM类型的资源  
    res=platform_get_resource(pdev,IORESOURCE_MEM,0);
    if(res==NULL)
    {
        printk("获取MEM类型资源失败\n");
        return -ENXIO;
    }
    //获取中断类型的资源
    irqno=platform_get_irq(pdev,0);
    if(irqno<0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }
    printk("mem资源%llx\n",res->start);
    printk("irq资源%d\n",irqno);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
//构建名字表
struct platform_device_id idtable[]=
{
    {"aaaaa",0},
    {"bbbbb",1},
    {"ccccc",2},
    {"ddddd",3},
    {},//防止数组越界
};

// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "bbbbb",
    },
    .id_table=idtable,//设置名字表匹配
};
//一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

二十二,设备树匹配实例

在stm32mp157a-fsmp1a.dts的根节点内部添加如下内容:
myplatform{
        compatible="hqyj,myplatform";
        reg=<0x11223344 59>;
       interrupt-parent=<&gpiof>;
       interrupts=<9 0>;
        led3-gpio=<&gpioe 8 0>;

    };

重新编译设备树,将镜像拷贝到~/tftpboot下

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include<linux/mod_devicetable.h>
#include<linux/of_gpio.h>
struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    //获取MEM类型的资源  
    res=platform_get_resource(pdev,IORESOURCE_MEM,0);
    if(res==NULL)
    {
        printk("获取MEM类型资源失败\n");
        return -ENXIO;
    }
    //获取中断类型的资源
    irqno=platform_get_irq(pdev,0);
    if(irqno<0)
    {
        printk("获取中断类型资源失败\n");
        return -ENXIO;
    }
    printk("mem资源%x\n",res->start);
    printk("irq资源%d\n",irqno);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    //设备树匹配成功后,设备树节点指针可以通过pdev->dev.of_node获取
    //基于设备树节点信息获取gpio_desc对象指针
    gpiono=gpiod_get_from_of_node(pdev->dev.of_node,"led3-gpio",0,GPIOD_OUT_HIGH,NULL);
        if(IS_ERR(gpiono))
        {
            printk("解析GPIO管脚信息失败\n");
            return -ENXIO;
        }
        printk("解析GPIO管脚信息成功\n");
    return 0;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    //释放GPIO信息
    gpiod_put(gpiono);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
//构建设备树匹配表
struct of_device_id oftable[] = {
    { .compatible = "hqyj,myplatform"},
    { /* end node */ },//防止数组越界
};


// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "bbbbb",
        .of_match_table=oftable,//用于设备树匹配
    },
};
//一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

二十三,综合练习,通过platform实现:

应用程序

#include <head.h>

int main(int argc, const char* argv[])
{
    int fd;
    int status;

    if ((fd = open("/dev/platform_irq_led", O_RDWR)) == -1) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    while (1) {
        read(fd, &status, sizeof(status));
        printf("status = %d\n", status);
    }
    close(fd);
    return 0;
}

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
/*
myplatform{
    compatible ="hqyj,platform";
    reg = <0x12345678 0x14>;
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>; 
    led1=<&gpioe 10 0>; 
};
*/
#define CNAME "platform_irq_led"
int major;
struct class *cls;
struct device *dev;
wait_queue_head_t wq;
unsigned int condition=0;
unsigned int status=0;
struct gpio_desc *desc;
unsigned int irqno;

irqreturn_t irq_led_handle(int irq, void *dev)
{
    status = gpiod_get_value(desc);//获取led管脚状态值
    status = !status;//状态值取反
    gpiod_set_value(desc,status);//重写管脚状态值

    condition=1;
    wake_up_interruptible(&wq);

    return IRQ_HANDLED;
}
int irq_led_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t irq_led_read(struct file *file, 
    char __user *ubuf, size_t size, loff_t *offs)
{
    int ret;
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    if(file->f_flags & O_NONBLOCK){
        //非阻塞
        return -EINVAL;
    }else{
        //阻塞
        ret = wait_event_interruptible(wq,condition);//进程休眠
        if(ret < 0){
            printk("receive signal....\n");
            return ret;
        }
    }
    //将数据拷贝到用户空间
    if(size > sizeof(status)) size = sizeof(status);
    ret = copy_to_user(ubuf,(void *)&status,size);
    if(ret){
        printk("copy data to user error\n");
        return -EIO;
    }

    //4.将条件清零
    condition = 0;

    return size;
}
int irq_led_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
struct file_operations fops = {
    .open = irq_led_open,
    .read = irq_led_read,
    .release = irq_led_close,
};
int pdrv_probe(struct platform_device* pdev) //在进入probe函数的时候节点已经被放在pdev->dev.of_node中了
{
   int ret;
    //1.注册字符设备驱动
    major = register_chrdev(0,CNAME,&fops);
    if(major < 0){
        printk("register char device driver error\n");
        ret = major;
        goto ERR1;
    }
    //2.创建设备节点
    cls = class_create(THIS_MODULE,CNAME);
    if(IS_ERR(cls)){
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR2;
    }
    dev = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME);
    if(IS_ERR(dev)){
        printk("device create error\n");
        ret = PTR_ERR(dev);
        goto ERR3;
    }
    //3.初始化等待队列头
    init_waitqueue_head(&wq);
   
    desc = gpiod_get_from_of_node(pdev->dev.of_node,"led1",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(desc)){
        printk("get gpiod error\n");
        ret = PTR_ERR(desc);
        goto ERR4;
    }

    irqno = platform_get_irq(pdev,0);  //获取中断的信息
    if(irqno < 0){
        printk("get irq resource error\n");
        ret = irqno;
        goto ERR5;
    }
    
    ret = request_irq(irqno,irq_led_handle,IRQF_TRIGGER_FALLING,CNAME,NULL);
    if(ret){
        printk("request irq error\n");
        goto ERR5;
    }

    return 0;
ERR5:
    gpiod_put(desc);
ERR4:
    device_destroy(cls,MKDEV(major,0));
ERR3:
    class_destroy(cls);
ERR2:
    unregister_chrdev(major,CNAME);
ERR1:
    return ret;
}
int pdrv_remove(struct platform_device* pdev)
{
    printk("%s:%d\n", __func__, __LINE__);
    free_irq(irqno,NULL);
    gpiod_put(desc);
    device_destroy(cls,MKDEV(major,0));
    class_destroy(cls);
    unregister_chrdev(major,CNAME);
    return 0;
}
struct of_device_id oftable[] = {
    {.compatible = "hqyj,platform",},
    {/*end*/},
};

//分配了一个驱动信息对象
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "hahaha",
        .of_match_table = oftable,
    },
    
};
//这个宏能够实现入口,出口,注册,注销的过程
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

二十四,IIC设备驱动编程实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/i2c.h>
//给对象分配空间并且初始化
int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    //字符设备驱动的注册
    //设备节点的创建
    //设备信息的获取。。。
    return 0;
}
int i2c_remove(struct i2c_client *client)
{
    //设备信息的注销
    //设备节点的销毁
    //驱动的注销
    return 0;
}
//定义设备树匹配的表
struct of_device_id oftable[]={
    {.compatible="hqyj,si7006",},
    {},
};
struct i2c_driver i2c_drv={
    .probe=i2c_probe,
    .remove=i2c_remove,
    .driver={
        .name="si7006",
        .of_match_table=oftable,  
    },
};
module_i2c_driver(i2c_drv);
MODULE_LICENSE("GPL");

二十五,IIC读取温湿度实例

头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#define GET_HUM  _IOR('m',1,int)//获取湿度的功能码
#define GET_TEM  _IOR('m',0,int)//获取温度的功能码


#endif

应用程序

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include<string.h>
#include <sys/ioctl.h>  
#include <arpa/inet.h>
#include"head.h"
int main(int argc, char const *argv[])
{
    int tem,hum;
    float tem1,hum1;
    int fd=open("/dev/si7006",O_RDWR);
    if(fd<0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }
    while(1)
    {
        //获取数据
        ioctl(fd,GET_HUM,&hum);
        ioctl(fd,GET_TEM,&tem);
          //大小端转换
        hum=ntohs(hum);
        tem=ntohs(tem);
        //计算数据
        hum1=125.0*hum/65536-6;
        tem1=175.72*tem/65536-46.85;
        printf("tem=%f,hum=%f\n",tem1,hum1);
        sleep(1);
    }
    return 0;
}

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include "head.h"
unsigned int major;
struct class *cls;
struct device *dev;
struct i2c_client *client1;
// 封装函数读取温度和湿度
int read_hum_tem(char reg)
{
    // 封装传输的消息
    char r_buf[] = {reg};
    short value;
    struct i2c_msg r_msg[] = {
        [0] = {
            .addr = client1->addr,
            .flags = 0,
            .len = sizeof(r_buf),
            .buf = r_buf,
        },
        [1] = {
            .addr = client1->addr,
            .flags = 1,
            .len = 2,
            .buf = (char *)&value,
        },
    };
    //传输消息
    int ret=i2c_transfer(client1->adapter,r_msg,2);
    if(ret!=2)
    {
        printk("传输消息失败\n");
        return -EIO;
    }
    
    return value;
}
// 封装操作方法
int si7006_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int tem, hum;
    int ret;
    switch (cmd)
    {
    case GET_HUM: // 读取湿度
        // 读取湿度的逻辑
        hum = read_hum_tem(0XE5);
        ret = copy_to_user((void *)arg, &hum, 4);
        if (ret)
        {
            printk("copy_to_user err\n");
            return ret;
        }
        break;
    case GET_TEM: // 读取温度
        // 读取温度的逻辑
        tem = read_hum_tem(0XE3);
        ret = copy_to_user((void *)arg, &tem, 4);
        if (ret)
        {
            printk("copy_to_user err\n");
            return ret;
        }
        break;
    }
    return 0;
}
int si7006_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义操作方法结构体遍历并且初始化
struct file_operations fops = {
    .open = si7006_open,
    .unlocked_ioctl=si7006_ioctl,
    .release = si7006_close,
};
// 给对象分配空间并且初始化
int i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    client1=client;
    int ret;
    // 字符设备驱动的注册
    major = register_chrdev(0, "si7006", &fops);
    if (major < 0)
    {
        printk("注册字符设备驱动失败\n");
        ret = major;
        goto out1;
    }
    printk("注册字符设备驱动成功\n");
    // 设备节点的创建
    // 向上提交目录
    cls = class_create(THIS_MODULE, "si7006");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = PTR_ERR(cls);
        goto out2;
    }
    printk("向上提交目录信息成功\n");
    // 向上提交设备节点信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "si7006");
    if (IS_ERR(dev))
    {
        printk("向上提交设备节点信息失败\n");
        ret = PTR_ERR(dev);
        goto out3;
    }
    printk("向上提交设备节点信息成功\n");

        return 0;
out3:
    class_destroy(cls);
out2:
    unregister_chrdev(major, "si7006");
out1:
    return ret;
}

int i2c_remove(struct i2c_client *client)
{
    // 设备信息的注销
    // 设备节点的销毁
    // 驱动的注销
    return 0;
}
// 定义设备树匹配的表
struct of_device_id oftable[] = {
    {
        .compatible = "hqyj,si7006",
    },
    {},
}; // 名字表的构建

// 分配驱动信息对象
struct i2c_driver i2c_drv = {
    .probe = i2c_probe,
    .remove = i2c_remove,
    .driver = {
        .name = "si7006",
        .of_match_table = oftable,
    },
};
// 一键注册宏
module_i2c_driver(i2c_drv);
MODULE_LICENSE("GPL");

二十五,SPI实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/spi/spi.h>

int m74hc595_probe(struct spi_device *spi)
{
    printk("%s:%d\n",__FILE__,__LINE__);
    return 0;
}
int m74hc595_remove(struct spi_device *spi)
{
     printk("%s:%d\n",__FILE__,__LINE__);
     return 0;
}

//设备树匹配表
struct of_device_id of_table[]={
    {.compatible="hqyj,m74hc595"},
    {},
};
//定义SPI对象并且初始化
struct spi_driver m74hc595 ={ 
    .probe=m74hc595_probe,
    .remove=m74hc595_remove,
    .driver={
        .name="m74hc595",
        .of_match_table=of_table,
    },
};
module_spi_driver(m74hc595); 
MODULE_LICENSE("GPL"); 

二十六,SPI点亮数码管

#include <linux/init.h>
#include <linux/module.h>
#include<linux/spi/spi.h>

int m74hc595_probe(struct spi_device *spi)
{
    char buf[]={0XF,0X6D};
    spi_write(spi,buf,sizeof(buf));
    printk("%s:%d\n",__FILE__,__LINE__);
    return 0;
}
int m74hc595_remove(struct spi_device *spi)
{
     printk("%s:%d\n",__FILE__,__LINE__);
     return 0;
}

//设备树匹配表
struct of_device_id of_table[]={
    {.compatible="hqyj,m74hc595"},
    {},
};
//定义SPI对象并且初始化
struct spi_driver m74hc595 ={ 
    .probe=m74hc595_probe,
    .remove=m74hc595_remove,
    .driver={
        .name="m74hc595",
        .of_match_table=of_table,
    },
};
module_spi_driver(m74hc595); 
MODULE_LICENSE("GPL"); 

二十八,块设备实例



#include <linux/init.h>
#include <linux/module.h>
#include<linux/blk-mq.h>
#include<linux/blkdev.h>
#include<linux/hdreg.h>
#include<linux/genhd.h>

//定义磁盘大小的宏定义
#define BLKSIZE (1*1024*1024)  //1M
//定义对象指针
struct gendisk *disk;
int major;
struct request_queue *queue; 
struct blk_mq_tag_set tag;
char *dev_addr=NULL;//申请磁盘空间的首地址

int mydisk_open(struct block_device *blkdev, fmode_t mode)
{
    printk("%s:%d\n",__FILE__,__LINE__);
    return 0;
}
void mydisk_close(struct gendisk *gdisk, fmode_t mode) 
{
    printk("%s:%d\n",__FILE__,__LINE__);

}
//用于设置磁盘的磁头、磁道以及扇区个数
int mydisk_getgeo(struct block_device *blkdev, struct hd_geometry *hd)
{
    printk("%s:%d\n",__FILE__,__LINE__);
    //设置磁头
    hd->heads=2;
    hd->cylinders=4;//磁道
    hd->sectors=2048/hd->heads/hd->cylinders;//扇区

    return 0;
}

struct block_device_operations fops={
    .open=mydisk_open,
    .release=mydisk_close,
    .getgeo=mydisk_getgeo,
};
//用于处理读写请求
blk_status_t handler_queue_rq(struct blk_mq_hw_ctx *ctx,
                 const struct blk_mq_queue_data *data)
{
    //数据读写
    
    return 0;
}

//队列操作方法结构体
struct blk_mq_ops ops={
.queue_rq =handler_queue_rq,
};
static int __init mycdev_init(void)
{
      //分配对象
    disk=alloc_disk(4);
    if(disk==NULL)
    {
        printk("分配对象失败\n");
        return -ENOMEM;
    }
    printk("分配对象成功\n");
    //申请设备号
     major=register_blkdev(0, "mydisk");
     if(major<0)
     {
        printk("申请设备号失败\n");
        return major;
     }
      printk("申请设备号成功\n");
      //队列设置
      queue=blk_mq_init_sq_queue(&tag,&ops,3,BLK_MQ_F_SHOULD_MERGE);
      if(IS_ERR(queue))
      {
        printk("队列设置失败\n");
        return PTR_ERR(queue);
      }
      printk("队列设置成功\n");
      //初始化
      disk->major=major;
      disk->first_minor=0;
      strcpy(disk->disk_name,"mydisk");
      disk->fops=&fops;
      disk->queue=queue;
      //每个扇区大小为512字节
      // set_capacity(disk,BLKSIZE>>9);
    //申请内存
    //dev_addr=
    //注册
    add_disk(disk);      
    return 0;
}
static void __exit mycdev_exit(void)
{
    del_gendisk(disk);
    blk_cleanup_queue(queue);
   unregister_blkdev(major,"mydisk");
   put_disk(disk); 
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue模板语法【下】事件处理器,表单、自定义组件、通信组件

目录 一、事件处理器 1.1常用的事件修饰符 1.2常用的按键修饰符 二&#xff0c;vue中的表单 三、自定义组件 四&#xff0c;通信组件 一、事件处理器 1.1常用的事件修饰符 Vue的事件修饰符是用来改变事件的默认行为或者添加额外的功能。以下是一些常用的事件修饰符及其…

Github 上很火的开源网页图标

Devicon https://devicon.dev 一系列关于编程语言、设计和开发工具的图标&#xff0c;提供了字体格式和 SVG 代码可以直接应用在你的项目中 VSCode Icons https://github.com/microsoft/vscode-icons#readme 收藏了 Visual Studio Code 软件上的所有的图标 Weather Icons https…

【06】FISCOBCOS中的节点前置服务

WeBASE管理平台 微众银行开源的自研区块链中间件平台——WeBASE(WeBank Blockchain Application Software Extension) 是区块链应用和FISCO BCOS节点之间搭建的中间件平台。WeBASE屏蔽了区块链底层的复杂度,降低区块链使用的门槛,大幅提高区块链应用的开发效率,包含节点前置…

【深度学习】图像去噪(2)——常见网络学习

【深度学习】图像去噪 是在 【深度学习】计算机视觉 系列文章的基础上&#xff0c;再次针对深度学习&#xff08;尤其是图像去噪方面&#xff09;的基础知识有更深入学习和巩固。 1 DnCNN 1.1 网络结构 1.1.1 残差学习 1.1.2 Batch Normalization (BN) 1.1.2.1 背景和目标…

手机也可以将声音转为字幕!支持中英日韩4种语言

快去看看你的华为手机有没有这个功能——AI字幕&#xff0c;可以将手机里的音频转换为文字&#xff08;以字幕形式展现&#xff0c;可保存在手机备忘录&#xff09; AI字幕有什么用途&#xff1f; 1. 在听觉不太好使的环境下&#xff0c;将音频信息转化到视觉&#xff08;文本…

OpenGLES:单纹理贴图

一.概述 最近疏于写博客&#xff0c;接下来会陆续更新这段时间OpenGLES的一些开发过程。 前两篇OpenGLES的博客讲解了怎样使用OpenGLES实现相机普通预览和多宫格滤镜 在相机实现过程中&#xff0c;虽然使用到了纹理&#xff0c;但只是在生成一个纹理之后&#xff0c;使用纹理…

基于SpringBoot+Vue+支付宝支付的汽车租赁系统(可做毕设/课设)

技术栈 前后端分离 前端使用: Vue Element 后端使用: SpringBoot Mysql8.0 Mybatis 支付宝支付 功能 分为 管理员端 和 普通用户端 和 维修人员端 普通用户端 1.首页 展示所有品牌,汽车,公告,按关键字搜索汽车名 2.汽车详情页 展示汽车详情和评价 3.下单支付和退押金 立即下单…

vue3 - 开发和生产环境通过Mock模拟真实接口请求

GitHub Demo 地址 在线预览 在前端开发中&#xff0c;常常需要与后端接口进行交互。然而&#xff0c;在接口尚未实现或者正在开发的情况下&#xff0c;前端开发人员往往无法得到真实的接口数据&#xff0c;这给开发和测试工作带来了一定的困扰。对此&#xff0c;可以通过Mock模…

《动手学深度学习 Pytorch版》 7.7 稠密连接网络

7.7.1 从 ResNet 到 DenseNet DenseNet 可以视为 ResNet 的逻辑扩展。 ResNet 将函数展开为 f ( x ) x g ( x ) f(\boldsymbol{x})xg(\boldsymbol{x}) f(x)xg(x)&#xff0c;即一个简单的线性项和一个复杂的非线性项。 若将 f f f 拓展成超过两部分&#xff0c;则 Dense…

CRM客户管理系统英文专业版

外资公司日常沟通的语言以英文为主&#xff0c;业务往来也是涉及到国内外&#xff0c;专业的英文版CRM系统很适合这样的业务团队&#xff0c;尤其CRM供应商是国际化企业&#xff0c;在海外也有分公司、办事处。 多语言 ZOHO支持多语种如英语、汉语、日语等28种语言&#xff0…

Next.js 13.5 正式发布,速度大幅提升!

9 月 19 日&#xff0c;Next.js 13.5 正式发布&#xff0c;该版本通过以下方式提高了本地开发性能和可靠性&#xff1a; 本地服务器启动速度提高 22%&#xff1a;使用App和Pages Router可以更快地进行迭代 HMR&#xff08;快速刷新&#xff09;速度提高 29%&#xff1a;在保存…

C++之指向引用的指针和指向指针的引用总结(二百三十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【计算机视觉】图像的获取和表示——图像传感器技术|主要参数解析、成像原理剖析、传感器处理

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生&#x1f338;博主主页&#xff1a; 是瑶瑶子啦每日一言&#x1f33c;: 每一个不曾起舞的日子&#xff0c;都是对生命的辜负。——尼采 前言 文章目录 前言一、图像传感器技术1.0&#xff1a;前言1.1&#xff1a;两种…

自定义类型详解(上)

结构体 1 结构体的声明 1.1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag//struct是结构体的标志&#xff0c;tag是标签;名字。 {member-list;//成员变量 }variable-list;//变量列…

Nginx 关闭/屏蔽 PUT、DELETE、OPTIONS 请求

1、修改 nginx 配置 在 nginx 配置文件中&#xff0c;增加如下配置内容&#xff1a; if ($request_method !~* GET|POST|HEAD) {return 403; }修改效果如下&#xff1a; 2、重启 nginx 服务 systemctl restart nginx或者 service nginx restart3、功能验证 使用如下方式…

【计算机网络】互联网公司的网络架构和业务场景

互联网公司的网络架构和业务场景 1. 互联网公司网络的组成1.1 网络的物理组成1.2 骨干网组成1.3 数据中心网络组成 2.互联网公司网络服务场景2.1 通用服务场景2.1.1 客户端到服务端请求真实网络过程2.1.2 客户端到服务端请求抽象网络过程2.1.3 负载均衡网络模型 2.2 边缘服务场…

Xilinx FPGA 程序固化重新上电程序不运行的问题

问题描述 FPGA直接下载bit文件,功能正常。 FPGA擦除FLASH,烧写FLASH,正常。 电源断电,重新上电,FALSH里面的程序没有启动,FPGA程序没有跑起来。–FLASH启动不正常。 解决办法 在XDC约束文件里边增加约束: ## Configuration options, can be used for all designs se…

京东获得JD商品详情 API 返回值说明

京东商品详情API接口可以获得JD商品详情原数据。 这个API接口有两种参数&#xff0c;公共参数和请求参数。 公共参数有以下几个&#xff1a; apikey&#xff1a;这是您自己的API密钥&#xff0c;可以在京东开发者中心获取。 请求参数有以下几个&#xff1a; num_iid&#…

11 FPGA_简易电压表设计与验证(附代码)

1. 模数转换理论 模数转换器又称&#xff08;A/D转换器&#xff09;&#xff0c;通常是指一个将模拟信号转变为数字信号的电子元件或电路。常见的转换方式使将模拟量与基准量比对得到便于传输的二进制信号。生活中常见的模拟量有温湿度、图像、声音等。模拟信号与数字信号的转换…

Spring学习笔记4 Bean的作用域

Spring学习笔记3 Spring对IOC的实现_biubiubiu0706的博客-CSDN博客 新建模块 spring-004 引入依赖 <dependencies><!--Spring依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId>&…