头文件
#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 RCC 0x50000A28
#define LED1_ADDR 0x50006000
#define LED2_ADDR 0x50007000
#define LED3_ADDR 0x50006000#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)
#endif
应用层文件
#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 fd;fd=open("/dev/myled0",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}int a,b;while (1){printf("请输入要实现的功能\n");printf("0关灯,1开灯\n");printf("请输入>");scanf("%d",&a);printf("请输入控制的灯\n");printf("1(LED1)2(LED2)3(LED3)\n");scanf("%d",&b);switch (a){case 1:ioctl(fd,LED_ON,&b);break;case 0:ioctl(fd,LED_OFF,&b);break;default:printf("输入错误\n");break;}}close(fd);return 0;
}
驱动文件
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>#include<linux/slab.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>#include"head.h"struct cdev *cdev;
unsigned int major=0;//主设备号
unsigned int minor=0;//次设备号char kbuf[128]={0};struct class *cls;
struct device *dev;unsigned int *vir_rcc;
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;dev_t devno;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;}return 0;
}
long mycdev_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{int wh;int ret=copy_from_user(&wh,(void*)arg,4);if(ret){printk("copy_from_user filed\n");return ret;}switch(cmd){case LED_ON:switch(wh){case 1:vir_led1->ODR |= (1<<10);break;case 2:vir_led2->ODR |= (1<<10);break;case 3:vir_led3->ODR |= (1<<8);break;} break;case LED_OFF:switch(wh){case 1:vir_led1->ODR &= (~(1<<10));break;case 2:vir_led2->ODR &= (~(1<<10));break;case 3:vir_led3->ODR &= (~(1<<8));break;} break;}return 0;
}
int mycdev_close(struct inode *inode,struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}
int all_led_init(void)
{vir_led1=ioremap(LED1_ADDR,sizeof(gpio_t));if(vir_led1==NULL){printk("led1内存映射失败");return -ENOMEM;}printk("led1内存映射成功");vir_led2=ioremap(LED2_ADDR,sizeof(gpio_t));if(vir_led2==NULL){printk("led2内存映射失败");return -ENOMEM;}printk("led2内存映射成功");vir_led3=ioremap(LED3_ADDR,sizeof(gpio_t));if(vir_led3==NULL){printk("led3内存映射失败");}printk("led3内存映射成功");vir_rcc=ioremap(RCC,4);if(vir_rcc==NULL){printk("rcc内存映射失败");}printk("rcc内存映射成功");//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_led3->MODER |= (1<<16);vir_led3->ODR &= (~(1<<8));printk("寄存器初始化成功\n");return 0;
}
struct file_operations fops=
{.open=mycdev_open,.read=mycdev_read,.write=mycdev_write,.unlocked_ioctl=mycdev_ioctl,.release=mycdev_close,
};
static int __init mycdev_init(void)
{int ret;//申请字符设备的驱动空间cdev = cdev_alloc();if (cdev == NULL){return -EFAULT;}printk("字符设备驱动对象申请成功\n");//初始化驱动对象cdev_init(cdev,&fops);//动态申请if(major==0){ret=alloc_chrdev_region(&devno,minor, 3,"myled");if(ret){printk("动态申请设备号失败\n");goto out1;}major=MAJOR(devno);minor=MAJOR(devno);}//静态指定else{ret=register_chrdev_region(MKDEV(major,minor), 3,"myled");if(ret){printk("静态申请设备号失败\n");goto out1;}}printk("设备号申请成功\n");//注册驱动ret=cdev_add(cdev,MKDEV(major,minor),3);if(ret){printk("注册驱动失败\n");goto out2;}printk("注册驱动成功\n");//向上提交目录cls=class_create(THIS_MODULE,"led");if(IS_ERR(cls)){printk("向上提交目录失败\n");ret=-PTR_ERR(cls);goto out3;}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 out4;}}printk("向上提交设备信息成功\n");//寄存器映射以及初始化all_led_init();return 0;
out4:
//销毁提交设备成功的设备信息for ( --i; i>=0; i--){device_destroy(cls,MKDEV(major,i));/* code */}class_destroy(cls);
out3:cdev_del(cdev);
out2:unregister_chrdev_region(MKDEV(major,minor),3);
out1:kfree(cdev);return ret;}
static void __exit mycdev_exit(void)
{//取消地址映射iounmap(vir_rcc); iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_led3);//销毁设备节点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");
makefile
arch?=arm
modname?=register_dev_c
ifeq ($(arch),arm)
KERNELDIR:=/home/ubuntu/linux-5.10.61
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)all:make -C $(KERNELDIR) M=$(PWD) modules
clean:make -C $(KERNELDIR) M=$(PWD) clean
obj-m:= $(modname).o
执行效果
开发板效果