驱动代码整理

一,控制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); // 在终端输入数据传递到bufbuf[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'://LED1if(kbuf[1]=='0')//关灯vir_led1->ODR &= (~(1<<10));else//开灯vir_led1->ODR |= 1<<10;break;case '2'://LED2if(kbuf[1]=='0')//关灯vir_led2->ODR &= (~(1<<10));else//开灯vir_led2->ODR |= 1<<10;break;case '3'://LED3if(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);//led1vir_led1->MODER &= (~(3<<20));vir_led1->MODER |= (1<<20);vir_led1->ODR &= (~(1<<10));//led2vir_led2->MODER &= (~(3<<20));vir_led2->MODER |= (1<<20);vir_led2->ODR &= (~(1<<10));//led3vir_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'://LED1if(kbuf[1]=='0')//关灯vir_led1->ODR &= (~(1<<10));else//开灯vir_led1->ODR |= 1<<10;break;case '2'://LED2if(kbuf[1]=='0')//关灯vir_led2->ODR &= (~(1<<10));else//开灯vir_led2->ODR |= 1<<10;break;case '3'://LED3if(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);//led1vir_led1->MODER &= (~(3<<20));vir_led1->MODER |= (1<<20);vir_led1->ODR &= (~(1<<10));//led2vir_led2->MODER &= (~(3<<20));vir_led2->MODER |= (1<<20);vir_led2->ODR &= (~(1<<10));//led3vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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: // LED1vir_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: // LED1vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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://控制LED1switch(cmd){case LED_ON://开灯break;case LED_OFF://关灯break;                                }break;case 1://控制LED2break;case 2://控制LED3break;}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: // LED1vir_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: // LED1vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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: // LED1vir_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: // LED1vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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: // LED1vir_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: // LED1vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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 led1virt_led1->MODER &= ~(3 << 20);virt_led1->MODER |= (1 << 20); // outputvirt_led1->ODR &= ~(1 << 10); // led1 off// init led2virt_led2->MODER &= ~(3 << 20);virt_led2->MODER |= (1 << 20); // outputvirt_led2->ODR &= ~(1 << 10); // led2 off// init led3virt_led3->MODER &= ~(3 << 16);virt_led3->MODER |= (1 << 16); // outputvirt_led3->ODR &= ~(1 << 8); // led3 offreturn 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: // LED1vir_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: // LED1vir_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);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_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;      // 用于操作epollstruct 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;           //填充事件的fdevent.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); //删除监听的客户端fdcontinue;}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: // LED1gpiod_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: // LED1gpiod_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.rhkb.cn/news/139642.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷刷题入门篇:分支结构

今天又来了&#xff0c;刷题刷题&#xff0c;我爱刷题&#xff0c;题单链接如下&#xff1a; https://www.luogu.com.cn/training/101#problems 一、【深基1-2】小学数学 N 合一 题目如下&#xff1a;https://www.luogu.com.cn/problem/P2433 题目描述 问题 1 请输出 I lov…

【C语言】指针经典笔试题(上)

C语言的一大重头戏就是指针。 对于指针有一些认识&#xff1a; 1.指针是存放变量的地址&#xff0c;一般说的指针和指针变量是一个概念。 2.地址的单位是字节&#xff0c;大小在不同编译器环境下有所不同&#xff0c;32位机器是4个字节&#xff0c;64位机器是8个字节。 3.数组名…

C# ref 学习1

ref 关键字用在四种不同的上下文中&#xff1b; 1.在方法签名和方法调用中&#xff0c;按引用将参数传递给方法。 2.在方法签名中&#xff0c;按引用将值返回给调用方。 3.在成员正文中&#xff0c;指示引用返回值是否作为调用方欲修改的引用被存储在本地&#xff0c;或在一般…

大厂面试-16道面试题

1 java集合类有哪些&#xff1f; List是有序的Collection&#xff0c;使用此接口能够精确的控制每个元素的插入位置&#xff0c;用户能根据索引访问List中元素。常用的实现List的类有LinkedList&#xff0c;ArrayList&#xff0c;Vector&#xff0c;Stack。 ArrayList是容量…

C语言内存函数的使用、剖析及模拟实现

目录 一、内存拷贝函数——memcpy 1.函数声明&#xff1a; 注意&#xff1a; 2.函数使用用例&#xff1a; 3.memcpy函数的模拟实现&#xff1a; 二、内存拷贝函数2——memmove 1.函数声明&#xff1a; 2.memmove函数的模拟实现 三、内存比较函数——memcmp 1.函数声明…

卸载Visual Studio 2010学习版 —— 卸载VCExpress

目录 最初安装Visual Studio 2010学习版是因为计算机二级 C语言考试而装&#xff0c;现如今考完试后便可卸载掉了&#xff0c;安装简便而卸载却没有uninstall.exe文件。故本文提供卸载方式。 进入到程序目录&#xff0c;找到setup.exe文件&#xff0c;也可以在程序目录搜索set…

【lesson10】进程状态

文章目录 认识进程状态新建运行阻塞挂起 Linux具体的进程状态RSDtTXZ是什么为什么 认识进程状态 上面就是各种进程状态&#xff0c;上面都是理论进程状态理论进程状态放在哪个操作系统中都是正确的&#xff0c;但是具体的操作系统实现可能又会有所不同。 下面我们来理解进程状态…

springBoot对接多个mq并且实现延迟队列---未完待续

mq调用流程 创建消息转换器 package com.wd.config;import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import o…

Vue的详细教程--Vue路由与nodejs

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Vue的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Vue路由是什么 二.使用Vue路由的步骤 1、…

小程序中如何划分会员级别以及不同级别不同积分累计、折扣、返佣、升级条件等

以下是一些关键步骤和注意事项&#xff0c;帮助商家在小程序中有效设置会员卡等级和相关规则。 1. 设定会员卡等级。在管理员后台->会员管理处&#xff0c;点击“等级...”。会出现级别设置界面。 可以创建会员卡等级&#xff0c;并为每个等级设定名称和对应的规则。一般会员…

MyBatis 日志模块

文章目录 前言LogLogFactory日志应用JDBC 日志BaseJdbcLoggerConnectionLogger应用实现 总结 前言 日志在我们开发过程中占据了一个非常重要的地位&#xff0c;是开发和运维管理之间的桥梁&#xff0c;在Java中的日志框架也非常多&#xff0c;Log4j、Log4j2、slf4j等&#xff…

创建表

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 创建表 语法格式: create table 表名(列名1 数据类型,列名2 数据类型,... ,列名n, 数据类型 ); 练习:在czwbkl库中,创建一格test01表 跟大家说…

开发高性能知识付费平台:关键技术策略

引言 在构建知识付费平台时&#xff0c;高性能是确保用户满意度和平台成功的关键因素之一。本文将探讨一些关键的技术策略&#xff0c;帮助开发者打造高性能的知识付费平台。 1. 前端性能优化 使用CDN加速资源加载 使用内容分发网络&#xff08;CDN&#xff09;来托管和加…

Flask数据库之SQLAlchemy--介绍--链接数据库

目录 SQLAlchemy介绍 SQLAlchemy连接数据库 SQLAlchemy介绍 数据库是一个网站的基础&#xff01;&#xff01;&#xff01; 比如MySQL、MongoDB、SQLite、PostgreSQL等&#xff0c;这里我们以MySQL为例进行讲解。 SQLAlchemy是一个ORM框架 对象关系映射&#xff08;英语&…

第一个 Go 程序“hello,world“ 与 main 函数

第一个 Go 程序"hello&#xff0c;world" 与 main 函数 文章目录 第一个 Go 程序"hello&#xff0c;world" 与 main 函数一.创建“hello&#xff0c;world”示例程序二. “hello&#xff0c;world” 程序结构拆解三、main 函数四、Go 语言中程序是怎么编译…

Xpath使用

有如下网页&#xff1a; 需要选中“若出现" 操作如下&#xff1a; 打开Xpath Helper 选中"若出现",右击检查 复制对应的Xpath 在QUERY栏插入即可

[杂谈]-快速了解半波和全波整流

快速了解半波和全波整流 文章目录 快速了解半波和全波整流1、滤波2、半波整流器3、全波整流器4、常见问题 整流器是一种将交流信号转换为脉动直流信号以及将交流电转换为直流电的电子电路。 我们日常生活中几乎所有的电子项目都会用到它。 根据周期传导&#xff0c;本文我们介绍…

VisualBox QA

出现提示注册表错误&#xff0c;或者之前正常&#xff0c;重启VisualBox后&#xff0c;VM运行失败时&#xff0c;可通过正确卸载VisualBox&#xff0c;然后使用注册表清理软件(CCleaner)清理注册表后&#xff0c;重装VisualBox&#xff0c;即会正常。&#xff08;一般用这个能解…

自学网络安全的三个必经阶段(含路线图)

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入…

「UG/NX」BlockUI 选择小平面区域 Select Facet Region

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「UG/NX」BlockUI集合&#x1f4da;全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C」C/C程序设计「Win」Windows程序设计「DSA」数据结构与算法「File」数据文件格式 目录 控件说…