一,控制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");