美文网首页
基于(修剪)ARM-Linux下遥控器驱动

基于(修剪)ARM-Linux下遥控器驱动

作者: 大神华仔 | 来源:发表于2016-09-18 19:59 被阅读0次

    基于ARM-Linux下遥控器驱动

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/string.h>
    #include <linux/irq.h>
    #include <asm/irq.h>
    #include <linux/delay.h>
    #include <linux/interrupt.h>
    #include <mach/regs-gpio.h>  // <----相关的ARM GPIO配置头文件
    #include <mach/hardware.h>   //
    #include <asm/gpio.h>
    #include <plat/gpio-cfg.h>
    MODULE_LICENSE("Dual BSD/GPL");
    #define CHARDEVICE_MAJOR   0
    #define CHARDEVICE_MINOR   0
    //首个本类设备的次编号
    #define CHARDEVICE_COUNT   4
    //!要管辖4个本类设备LED
    #define CHARDEVICE_NAME    "con_device"
    static dev_t dev = 0;  //用于存首个设备编号
    static int state=0;
    
    #define IOCTL_GPIO_MOVE 1
    #define IOCTL_GPIO_BACK 0
    #define IOCTL_GPIO_STOP 2
    #define IOCTL_GPIO_TURN_LEFT 3
    #define IOCTL_GPIO_TURN_RIGHT 4
    #define IOCTL_GPIO_PIN 5
     
    
    static unsigned long gpio_table [] =
    {
     S3C2410_GPF3,
     S3C2410_GPF4,
     S3C2410_GPG0,
     S3C2410_GPG3,
    };
    
    static unsigned int gpio_cfg_table [] =
    {
     S3C2410_GPF3_INP,
     S3C2410_GPF4_INP,
     S3C2410_GPG0_INP,
     S3C2410_GPG3_INP,
    };
    
    static unsigned long gpio_irq_table [] = //作为中断源的引脚
    {
     S3C2410_GPF3,
     S3C2410_GPF4,
     S3C2410_GPG0,
     S3C2410_GPG3,
    };
    static unsigned long gpio_irq_cfg_table [] = //引脚设置为哪一号中断源
    {
     IRQ_EINT3,
     IRQ_EINT4,
     IRQ_EINT8,
     IRQ_EINT11,
    };
    static char* gpio_irq_name_cfg_table [4] = //给中断引脚一个标识符,相当于ID
    {
     "S3C2410_GPF3_EINT1",
     "S3C2410_GPF4_EINT4",
     "S3C2410_GPG0_EINT2",
     "S3C2410_GPG3_EINT0"
    };
    #define CHARDEVICE_CLASS_NAME "con_device_class"  
    //类名称相同,驱动将会有冲突
    //定义以下变量方便动态存主次编号
    static u32 chardevice_major=CHARDEVICE_MAJOR;
    static u32 chardevice_minor=CHARDEVICE_MINOR;
    static int irq_nLED=0;
    //用于自动配置节点文件
    static struct class *dev_class=NULL;
    struct self_cdev
    {
       struct device *dev_class_node;//设备节点对象
       struct cdev i_cdev;//字符设备驱动实例,内含两个重要参数:设备编号和服务接口结构
       u8 led;
       u8 bLighting;//具体设备实例的私有数据,第几号LED
    };//!用户自定义的字符设备结构<--方便具体设备的管理,比如指定设备的管理对象,它的私有数据(例如LED的当前电平状态变量)
    //!ioctl接口处理函数<--可以不用依赖open函数来得到file参数,它可以直接处理node,也可以通过file参数来直接操作(双通道);cmd表示控制命令参数;arg表示命令cmd的附加参数
    static int chardevice_ioctl(struct inode* node,struct file *filep,unsigned int cmd,unsigned long arg){
       struct self_cdev* scdevp=filep->private_data;//!接回容器实例首地址
      // printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);
     //  printk(KERN_ALERT "LED:%d\n",scdevp->led);
       return 0;  
    }
    //!open接口处理函数
    //参数node:用于传入指定的设备节点文件结构体;filep:用于回馈一个文件实例指针
    //!<--注意这里的接口处理函数的参数设置跟系统调用函数open的参数设置是不一样的
    static int chardevice_open(struct inode* node,struct file *filep){
       struct self_cdev* scdevp=NULL;//容器类型指针
       printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);
       scdevp=container_of(node->i_cdev,struct self_cdev,i_cdev);  
       //container_of作用是从节点文件所在的结构体中提取所对应设备的容器(包含了cdev等管理对象,也包含有设备的私有数据)
       //原型:container_of(节点的i_cdev,自定义的容器结构类型,容器结构中的cdev成员名)
       scdevp->led=MINOR(node->i_rdev);
       filep->private_data=scdevp;//!让文件实例指针能透过成员private_data全面管辖到指定的设备
       return 0;  
    }
    static int chardevice_read(struct file *filep,char __user *buf,size_t count,loff_t *offset){
       struct self_cdev* scdevp=filep->private_data;//!接回容器实例首地址
       int i,j,k,l;
       i=s3c2410_gpio_getpin(gpio_table[0]);
       j=s3c2410_gpio_getpin(gpio_table[1]);
       k=s3c2410_gpio_getpin(gpio_table[2]);
       l=s3c2410_gpio_getpin(gpio_table[3]);
      
       if(i!=0){
       strcpy(buf,"w");
     
      }
       else if(k!=0){
       strcpy(buf,"r");
     
      }
      
       else if(j!=0){
       strcpy(buf,"b");
     
      }
       else if(l!=0){
       strcpy(buf,"l");
     
      }
       else if (i==0&&k==0&&l==0&&j==0)
          {
          strcpy(buf,"s");     
          }
     
       return 0;
    }
    //!release/close接口处理函数<--可以不用依赖open函数来得到file参数,它可以直接处理node,也可以通过file参数来直接操作(双通道)
    static int chardevice_release(struct inode* node,struct file *filep){
       struct self_cdev* scdevp=filep->private_data;//!接回容器实例首地址
       printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);
       printk(KERN_ALERT "LED:%d\n",scdevp->led);
       return 0;  
    }
    //!指定部分结构的成员进行初始化
    static struct file_operations i_cdev_fops = {
       .owner=THIS_MODULE, //预定义好的宏,表示本模块
        .open=chardevice_open, //指定open接口处理函数是右侧函数
       .ioctl=chardevice_ioctl,
        .read=chardevice_read,
       .release=chardevice_release
    };//类似QT里的connect()
    static struct self_cdev* i_scdev=NULL;
    
    static int chardevice_add(struct self_cdev* pscdev,int index)
    {
       dev_t ldev; int ret =-EFAULT;   int ret2 =-EFAULT;
       printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);  
       //!添加字符设备实例
       ldev=MKDEV(chardevice_major,chardevice_minor+index);//!建立设备编号
       cdev_init(&pscdev->i_cdev,&i_cdev_fops);//!绑定服务接口
       ret=cdev_add(&pscdev->i_cdev,ldev,1);//添加字符设备实例进内核
       if(ret<0){printk("Failture to add cdev to kernel!\n");
          return ret;
       }
       printk("Success to add cdev to kernel!\n");
       //!添加对应的设备节点文件<--不需要重复建立dev_calss
       pscdev->dev_class_node=
       device_create(dev_class,NULL,ldev,NULL,"%s%d",
                     CHARDEVICE_NAME,MINOR(ldev));
       if(pscdev->dev_class_node==NULL){
         printk("Can not create a device file:/dev/%s%d\n",CHARDEVICE_NAME,MINOR(ldev));
         ret=PTR_ERR(pscdev->dev_class_node);
         return ret;//提取dev_class_node中的错误代号给ret
       }
       printk("Success to create device node file:/dev/%s%d\n",CHARDEVICE_NAME,MINOR(ldev));
      
       s3c2410_gpio_cfgpin(gpio_table[index], gpio_cfg_table[index]);//配置对应LED的GPIO引脚为输出
       s3c2410_gpio_setpin(gpio_table[index], 1);
       //!s3c2410_gpio_cfgpin用于配置控制寄存器
       return 0;
    }
    
    static int __init chardevice_init(void)
    {
       int ret=-EFAULT, i=0,x=0;
       printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);
       //!注册设备编号
       if(chardevice_major!=0){//!手动指定生成设备编号
         dev=MKDEV(CHARDEVICE_MAJOR,CHARDEVICE_MINOR);//由主次编号合成  
         ret=register_chrdev_region(dev,CHARDEVICE_COUNT,
                                  CHARDEVICE_NAME);//在系统中注册
       }
       else {//!由系统自动动态生成设备编号
         ret=alloc_chrdev_region(&dev,chardevice_minor,CHARDEVICE_COUNT,
                                  CHARDEVICE_NAME);//由alloc_chrdev_region动态确定一个空闲的设备编号供应给dev
         chardevice_major=MAJOR(dev);//在设备编号中提取主编号                       
       }
       //!错误处理 
         if(ret<0){
           printk("Can not reg char dev:Major:%d!\n",
                  chardevice_major); goto failure_reg_chrdev;
         }
           printk("Success to reg char dev:Major:%d!\n",
                  chardevice_major);
       //建立类对象
       dev_class=class_create(THIS_MODULE,CHARDEVICE_CLASS_NAME);//创建并限定类结构变量指向本模块,并指定类名称
       if(dev_class==NULL){printk("Can not create a device class:%s!\n",
          CHARDEVICE_CLASS_NAME); ret=PTR_ERR(dev_class);
          goto failure_create_class;//提取dev_class中的错误代号给ret
       } 
       printk("Success to create device class:%s!\n",CHARDEVICE_CLASS_NAME);  
     
       //!动态分配内核空间并在内核中添加一个(以上)自定义的字符设备实例
       i_scdev=
       kmalloc(sizeof(struct self_cdev)*CHARDEVICE_COUNT,GFP_KERNEL);
       if(i_scdev==NULL){printk("No enough memory for i_scdev!\n");
       ret=PTR_ERR(i_scdev);goto failure_alloc_scdev;}
       memset(i_scdev,0,sizeof(struct self_cdev)*CHARDEVICE_COUNT);
       for(i=0;i<CHARDEVICE_COUNT;i++){
          ret=chardevice_add(&i_scdev[i],i);//第二个参数指定次设备号偏移量(在首个设备次设备号基础)
          if(ret<0)goto failure_scdev_add;
       }
       return 0;
    //定义下边这些错误处理是因为在初始化__init里出问题,是不能用rmmod去反安装模块,也就不会调用到__exit相关的函数
    failure_scdev_add:
       for(x=0;x<i;x++){
       device_destroy(dev_class,MKDEV(chardevice_major,chardevice_minor+x));
       cdev_del(&(i_scdev[x].i_cdev));
       }
       class_destroy(dev_class);
    failure_create_class:
       kfree(i_scdev);
    failure_alloc_scdev:
       unregister_chrdev_region(dev,CHARDEVICE_COUNT);
    failure_reg_chrdev:
       return ret;
    }
    static void __exit chardevice_exit(void)
    {
       int x;
       printk(KERN_ALERT "Entry %s function!\n",__FUNCTION__);
      
       for(x=0;x<CHARDEVICE_COUNT;x++){
       //!清理指定设备的节点文件
       device_destroy(dev_class,MKDEV(chardevice_major,chardevice_minor+x));
       //!删除内核中的指定设备实例
       cdev_del(&(i_scdev[x].i_cdev));
       }
       //!删除类对象
       class_destroy(dev_class);
       //!释放内核堆空间
       kfree(i_scdev);
       //!注销设备编号
       unregister_chrdev_region(dev,CHARDEVICE_COUNT);
    }
    module_init(chardevice_init);
    module_exit(chardevice_exit);
    MODULE_AUTHOR("ELCO");
    MODULE_DESCRIPTION("...");
    MODULE_VERSION("1.0.0");
    MODULE_ALIAS("CharDevice!");
    

    相关文章

      网友评论

          本文标题:基于(修剪)ARM-Linux下遥控器驱动

          本文链接:https://www.haomeiwen.com/subject/hmjgettx.html