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

基于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!");
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容