1.1 编程要求
编写设备信息端 pdev.c
- 操作LED1灯的地址信息,编写在设备信息端 ===> LED1 / PE10
- GPIOE基地址 0x50006000
- RCC地址 0x50000A28
编写设备驱动端 pdrv.c
- 操作LED1灯的驱动相关内容,编写在设备驱动端
- 获取设备信息端地址信息
- 地址映射
- 对LED1灯进行初始化 设置GPIOE组时钟使能 设置PE10引脚输出模式 输出低电平
- 注册字符设备驱动
- 自动提交设备节点信息
- 通过ioctl进行点灯
编写应用层测试程序
- 在应用层程序test.c编写测试程序
/pdrv/demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "myled.h"
#include <linux/device.h>
unsigned int major = 0;
#define CNAME "myled"
char kbuf[128] = "";
unsigned int* rcc_virt = NULL;
unsigned int* gpioe_moder_virt = NULL;
unsigned int* gpioe_odr_virt = NULL;
gpio_t* gpioe_virt = NULL;
gpio_t* gpiof_virt = NULL;
struct class *cls;
struct device *device;
#define LED1_ON (gpioe_virt->ODR |= (0x1 << 10))
#define LED1_OFF (gpioe_virt->ODR &= (~(0x1 << 10)))
#define LED2_ON (gpiof_virt->ODR |= (0x1 << 10))
#define LED2_OFF (gpiof_virt->ODR &= (~(0x1 << 10)))
#define LED3_ON (gpioe_virt->ODR |= (0x1 << 8))
#define LED3_OFF (gpioe_virt->ODR &= (~(0x1 << 8)))
int myled_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
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 form user is error\n");
return -EIO;
}
//kbuf[0] = 0 LED1灯熄灭 kbuf[0] = 1 LED1灯点亮
if(kbuf[0] == 0){ //LED1灯熄灭
*gpioe_odr_virt &= (~(0x1 << 10));
}
else{ //LED1灯点亮
*gpioe_odr_virt |= (0x1 << 10);
}
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,
.write = myled_write,
.release = myled_close,
};
//当设备信息端和设备驱动端匹配成功时,执行probe函数
int pdrv_probe(struct platform_device *pdev)
{
struct resource* res;
int irqno;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
printk("name = %s id = %ld\n",pdev->id_entry->name,pdev->id_entry->driver_data);
//获取设备信息端地址信息
res = platform_get_resource(pdev,IORESOURCE_MEM,0);
if(res == NULL){
printk("platform get resource is error\n");
return -EIO;
}
//获取设备信息端中断号信息
irqno = platform_get_irq(pdev,0);
if(irqno < 0){
printk("platform get irq is error\n");
return -EIO;
}
//打印获取到的地址信息 中断号
printk("address = %#llx irqno = %d\n",res->start,irqno);
//注册字符设备驱动
major = register_chrdev(0,CNAME,&fops);
if(major < 0){
printk("register chrdev is error\n");
return -EIO;
}
printk("major = %d\n",major);//打印主设备号的值
//rcc物理地址映射
rcc_virt = ioremap(RCC_MP_AHB4ENSETR_PHY,4);
if(rcc_virt == NULL){
printk("rcc ioremap is error\n");
return -EIO;
}
//GPIO_MODER寄存器物理地址映射
gpioe_moder_virt = ioremap(GPIOE_MODER_PHY,4);
if(gpioe_moder_virt == NULL){
printk("gpioe moder ioremap is error\n");
return -EIO;
}
//GPIO_ODR寄存器物理地址映射
gpioe_odr_virt = ioremap(GPIOE_ODR_PHY,4);
if(gpioe_odr_virt == NULL){
printk("gpioe odr ioremap is error\n");
return -EIO;
}
//LED1灯初始化
*rcc_virt |= (0x1 << 4); //使能GPIOE组控制器
*gpioe_moder_virt &= (~(0x3 << 20)); //设置PE10引脚为输出模式
*gpioe_moder_virt |= (0x1 << 20);
*gpioe_odr_virt &= (~(0x1 << 10)); //设置PE10引脚输出低电平
//向上层提交目录信息
cls = class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls)){
return PTR_ERR(cls);
}
//向上层提交设备节点信息
device = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME);
if(IS_ERR(device)){
return PTR_ERR(device);
}
return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
int ret;
int whitch;
switch(cmd) //判断命令码
{
case LED_ON: //灯亮命令码
ret = copy_from_user(&whitch,(void*)args,GET_CMD_SIZE(LED_ON));
if(ret){
printk("copy from user is error\n");
return -EIO;
}
switch (whitch) //判断哪一盏灯点亮
{
case LED1:
LED1_ON; //LED1点亮
break;
case LED2:
LED2_ON; //LED2点亮
break;
case LED3:
LED3_ON; //LED3点亮
break;
}
break;
case LED_OFF: //灯灭命令码
ret = copy_from_user(&whitch,(void*)args,GET_CMD_SIZE(LED_OFF));
if(ret){
printk("copy from user is error\n");
return -EIO;
}
switch (whitch) //判断哪一盏灯熄灭
{
case LED1:
LED1_OFF; //LED1熄灭
break;
case LED2:
LED2_OFF; //LED2熄灭
break;
case LED3:
LED3_OFF; //LED3熄灭
break;
}
break;
}
return 0;
}
//当任意一方卸载,执行remove函数
int pdrv_remove(struct platform_device *pdev)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
//取消地址映射
iounmap(rcc_virt);
iounmap(gpioe_moder_virt);
iounmap(gpioe_odr_virt);
//注销字符设备驱动
unregister_chrdev(major,CNAME);
device_destroy(cls,MKDEV(major,0)); //取消向上层提交设备节点信息
class_destroy(cls); //取消向生成提交目录信息
return 0;
}
const struct platform_device_id idtable[] = {
{"hello1",1},
{"hello2",2},
{"hello3",3},
{},/*防止数组越界*/
};
//初始化设备驱动端结构体
struct platform_driver pdrv = {
.probe = pdrv_probe,
.remove = pdrv_remove,
.driver = {
.name = "hello DC23111", //通过名字进行匹配
},
.id_table = idtable,
};
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
/pdrv/test.c
#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 "myled.h"
int main(int argc, const char *argv[])
{
int fd = -1;
int whitch;
char buf[128] = "";
fd = open("/dev/myled",O_RDWR);
if(fd == -1){
perror("open is error");
return -1;
}
while(1)
{
whitch = LED1;
ioctl(fd,LED_ON,&whitch);
sleep(1);
ioctl(fd,LED_OFF,&whitch);
sleep(1);
whitch = LED2;
ioctl(fd,LED_ON,&whitch);
sleep(1);
ioctl(fd,LED_OFF,&whitch);
sleep(1);
whitch = LED3;
ioctl(fd,LED_ON,&whitch);
sleep(1);
ioctl(fd,LED_OFF,&whitch);
sleep(1);
}
close(fd);
return 0;
}
/pdrv/myled.h
#ifndef __MYLED_H__
#define __MYLED_H__
enum{
LED1, //0
LED2, //1
LED3, //2
};
typedef struct {
volatile unsigned int MODER; // 0x00
volatile unsigned int OTYPER; // 0x04
volatile unsigned int OSPEEDR; // 0x08
volatile unsigned int PUPDR; // 0x0C
volatile unsigned int IDR; // 0x10
volatile unsigned int ODR; // 0x14
volatile unsigned int BSRR; // 0x18
volatile unsigned int LCKR; // 0x1C
volatile unsigned int AFRL; // 0x20
volatile unsigned int AFRH; // 0x24
volatile unsigned int BRR; // 0x28
volatile unsigned int res;
volatile unsigned int SECCFGR; // 0x30
}gpio_t;
#define RCC_MP_AHB4ENSETR_PHY 0x50000A28
#define GPIOE_MODER_PHY 0x50006000
#define GPIOE_ODR_PHY 0X50006014
#define GPIOE (0x50006000) //GPIOE组物理地址
#define GPIOF (0x50007000) //GPIOF组物理地址
#define RCC_MP_AHB4ENSETR_PHY 0x50000A28 //指定rcc物理地址
#define LED_ON _IOW('a',1,int) //封装灯亮命令码
#define LED_OFF _IOW('a',0,int) //封装灯灭命令码
#define GET_CMD_SIZE(cmd) (cmd >> 16 & 0x3fff) //封装命令码大小
#endif
/pdev/demo.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//描述设备信息结构体
struct resource res[] = {
[0] = {
.start = 0x50006000, //起始地址
.end = 0x50006000 + 31, //终止地址
.flags = IORESOURCE_MEM, //资源类型
},
[1] = {
.start = 71, //起始中断号
.end = 71+2,
.flags = IORESOURCE_IRQ,
},
};
void pdev_release(struct device *dev)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}
//初始化设备信息端结构体
struct platform_device pdev = {
.name = "hello1", //通过名字进行匹配
.id = PLATFORM_DEVID_AUTO, //自动分配
.dev = {
.release = pdev_release,
},
.num_resources = ARRAY_SIZE(res),
.resource = res,
};
static int __init demo_init(void)
{
//注册设备信息端
return platform_device_register(&pdev);
}
static void __exit demo_exit(void)
{
//注销设备信息端
platform_device_unregister(&pdev);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");