6.20 驱动开发作业

通过GPIO子系统编写LED驱动,应用程序控制LED灯亮灭

head.h
#ifndef __HEAD_H__
#define __HEAD_H__

//封装GPIO组寄存器结构体
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;

#define PHY_GPIOE_ADDR 0X50006000
#define PHY_GPIOF_ADDR 0X50007000
#define PHY_RCCAHB4_ADDR 0X50000A28  //使能GPIO章节

#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)




#endif
test.c
#include
#include
#include 
#include 
#include 
#include
#include
#include 
#include"head.h"

int main(int argc,char const *argv)
{
     char buf[128]={0};
     int a,b;

    //通过open函数打开字符驱动文件得到对应文件描述符后,用户层调用open,该字符设备驱动则也会调用对应的.open成员函数
     int fd=open("/dev/myleds",O_RDWR);
     if(fd<0)
     {
        printf("打开设备文件失败\n");
        exit(-1);
     }

     while(1)
    {
        printf("请输入要操控的设备:1(Led1灯) 2(Led2灯) 3(Led3灯)\n");
        scanf("%d",&a);
        printf("请输入: 1(亮、响) 0(灭/不响)\n");
        scanf("%d",&b);
        switch (a)
        {
        case 1:
            if(b==1)
            ioctl(fd,LED_ON,&a);
            else if(b==0)
            ioctl(fd,LED_OFF,&a);
            break;
        case 2:
            if(b==1)
            ioctl(fd,LED_ON,&a);
            else if(b==0)
            ioctl(fd,LED_OFF,&a);
            break;
        case 3:
            if(b==1)
            ioctl(fd,LED_ON,&a);
            else if(b==0)
            ioctl(fd,LED_OFF,&a);
            break;
        default:
            printf("输入有误\n");
            return 0;
        }
    }

     close(fd);
     return 0;
}
dtb.c
#include 
#include 
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "head.h"

/*    myleds{
    led1=<&gpioe 10 0>;
    led2=<&gpiof 10 0>;
    led3=<&gpioe 8 0>;
};*/

int gpiono1;
int gpiono2;
int gpiono3;
struct device_node *dnode;

char kbuf[128]={0};

//存储获得的设备号
unsigned int major=0;
unsigned int minor=0;

//存储最终设备号
dev_t devno;

//驱动设备对象指针
struct cdev *cdev;

//指向申请的向上提交目录和设备文件信息
struct class *cls;
struct device *dev;

//遍写设备驱动中的mycdev_ioctl给操作方法结构体变量赋值
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
   int ret2;
   int which;

   //注意:!!!!!想使用arg要先强转赋值给另一个变量
   ret2=copy_from_user(&which,(void *)arg,sizeof(int));

switch(which)
{
    case 1:
        switch(cmd)
        {
            case LED_ON:
              gpio_set_value(gpiono1,1);//开灯,第二个参数填1,高电平,0低电平
              break;
            case LED_OFF:
              gpio_set_value(gpiono1,0);//开灯,第二个参数填1,高电平,0低电平
              break;
        }
        break;
    case 2:
        switch(cmd)
        {
            case LED_ON:
              gpio_set_value(gpiono2,1);//开灯,第二个参数填1,高电平,0低电平
              break;
            case LED_OFF:
              gpio_set_value(gpiono2,0);//开灯,第二个参数填1,高电平,0低电平
              break;
        }
        break;
    case 3:
        switch(cmd)
        {
            case LED_ON:
              gpio_set_value(gpiono3,1);//开灯,第二个参数填1,高电平,0低电平
              break;
            case LED_OFF:
              gpio_set_value(gpiono3,0);//开灯,第二个参数填1,高电平,0低电平
              break;
        }
        break;
}
return 0;
}

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

 ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *iof)
 {
    int ret;

    //向用户拷贝
    if(size>sizeof(kbuf))
    {
        size=sizeof(kbuf);
    }

    //不需要将进程手动添加到等待队列里了
    //执行到这说明进程被write函数中的wake_up函数唤醒了
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("copy to user filed\n");
        return -EIO;
    }
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
   
    return 0;
 }


//struct file是每一个打开了的文件就会有的结构体
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *iof)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    
    int ret;
    
    //从用户拷贝
    if(size>sizeof(kbuf))
        size=sizeof(kbuf);

    //从用户获取数据保存在kbuf中
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy from user filed\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,
    .unlocked_ioctl=mycdev_ioctl,
    .release=mycdev_close,
};

static int __init mycdev_init(void)
{
    int ret;

     //1.分配字符设备驱动对象空间
    cdev=cdev_alloc();
    if(cdev==NULL)
    {
        printk("分配字符设备驱动对象失败\n");
        ret=-EFAULT;
        goto LOOP1;
    }
    printk("分配对象空间成功\n");

    //2.字符驱动对象初始化
    cdev_init(cdev,&fops);

    //3.申请设备号
    //静态指定设备号
    if(major>0)
    {
        ret=register_chrdev_region(MKDEV(major,minor),1,"myleds");
        if(ret)
        {
            printk("静态指定设备号失败\n");
            printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
            goto LOOP2;
        }
    }
    else if(major==0) //动态申请设备号
    {
        ret=alloc_chrdev_region(&devno,minor,1,"myleds");
        if(ret)
        {
            printk("动态申请设备号失败\n");
             goto LOOP2;
        }
        major=MAJOR(devno);
        minor=MINOR(devno);
    }
    printk("申请设备号成功\n");
    
    //4.添加字符设备驱动对象注册进内核
    ret=cdev_add(cdev,MKDEV(major,minor),1);
    if(ret)
    {
        printk("字符设备驱动对象注册失败\n");
        goto LOOP3;
    }
    printk("添加字符设备驱动对象注册进内核成功\n");

     //向上提交目录(给udev机制使用,从而能在/dev下创建设备文件)
    cls=class_create(THIS_MODULE,"myleds");
    if(IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
        goto LOOP4;
    }
    printk("向上提交目录成功\n");

    //向上提交设备节点信息(给udev机制使用,从而能在/dev下创建设备文件)
    dev=device_create(cls,NULL,MKDEV(major,0),NULL,"myleds");
    if(IS_ERR(dev))
    {
        printk("向上提交设备节点失败\n");
        ret= -PTR_ERR(dev);
        goto LOOP5;
    }

    printk("向上提交设备节点成功\n");

   //解析需要的设备树节点,先要自行在设备树文件中添加节点
   //返回找到的设备树节点首地址
   dnode=of_find_node_by_name(NULL,"myleds");
   if(dnode==NULL)
   {
    printk("解析设备树节点失败\n");
    return -ENOMEM;
   }
   printk("解析设备树节点成功\n");

   //根据设备树节点首地址解析出gpio编号,
   //通过键名就可通过内核中添加的设备树属性自动判断出偏移后的编号,为gpioe10
    gpiono1= of_get_named_gpio(dnode,"led1",0);
    if(gpiono1<0)
    {
        printk("解析设备号失败\n");
        return -EIO;
    }

    //拿着解析出的编号申请gpioe10编号
   ret=gpio_request(gpiono1,NULL);
   if(ret)
   {
    printk("GPIO编号申请失败\n");
    return -EIO;
   }
   printk("申请gpio编号成功%d\n",gpiono1);

   //设置引脚为输出,第二个参数为默认值,gpio_direction_input为输入
   gpio_direction_output(gpiono1,0);


    gpiono2= of_get_named_gpio(dnode,"led2",0);
    if(gpiono2<0)
    {
        printk("解析设备号失败\n");
        return -EIO;
    }

   ret=gpio_request(gpiono2,NULL);
   if(ret)
   {
    printk("GPIO编号申请失败\n");
    return -EIO;
   }
   printk("申请gpio编号成功%d\n",gpiono2);

   gpio_direction_output(gpiono2,0);

    gpiono3= of_get_named_gpio(dnode,"led3",0);
    if(gpiono3<0)
    {
        printk("解析设备号失败\n");
        return -EIO;
    }

   ret=gpio_request(gpiono3,NULL);
   if(ret)
   {
    printk("GPIO编号申请失败\n");
    return -EIO;
   }
   printk("申请gpio编号成功%d\n",gpiono3);

   gpio_direction_output(gpiono3,0);

    return 0;
    
    LOOP5:
        //释放已经申请的设备节点信息
        device_destroy(cls,MKDEV(major,0));

        //释放目录空间
        class_destroy(cls);

    LOOP4:
        //注销字符设备驱动对象
        cdev_del(cdev);

    LOOP3:
        //释放设备号
        unregister_chrdev_region(MKDEV(major,minor),1);

    LOOP2:
        kfree(cdev);    

    LOOP1:
        return ret; 
}

static void __exit mycdev_exit(void)
{
    //释放gpio编号
    gpio_free(gpiono1);
    gpio_free(gpiono2);
    gpio_free(gpiono3);

    //销毁设备节点信息
    device_destroy(cls,MKDEV(major,0));

    //销毁目录空间
    class_destroy(cls);

    //注销字符设备驱动对象
    cdev_del(cdev);

    //释放设备号
    unregister_chrdev_region(MKDEV(major,minor),1);

    //释放对象空间
    kfree(cdev);

}

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

你可能感兴趣的:(笔记,驱动开发)