linux-3.0.1下ok6410的led字符设备驱动

来源:互联网 发布:python exec函数 编辑:程序博客网 时间:2024/05/22 20:27
驱动程序名为led.c,代码如下:
#include <linux/kernel.h>       //以便使用printk()等函数
#include <linux/module.h>   //编写任何模块都需要包含此头文件,包含宏MODULE_LICENSE(‘GPL’)
#include <linux/init.h>             //必须的,定义了module_init()与module_exit()宏 
#include <linux/fs.h>               //包含常用的数据结构,如struct file等
#include <linux/cdev.h>         //该头文件定义了struct cdev数据结构,也包含了字符设备操作的相关函数
#include <linux/pci.h>            //包含readl,wrietl
#include <asm/uaccess.h>    //copy_to_user,copy_from_user 
#include <mach/map.h>      
#include <mach/regs-gpio.h>   //定义了GPIO相关的数据宏
#include <plat/gpio-cfg.h>    //包含了GPIO的各种配置函数,如设置输入输出,设置上拉电阻等
#include <mach/gpio-bank-m.h> //以便于使用s3c64xx_GPM   
      
#define DEVICE_NAME "led"   //定义设备名
#define LED_MAJOR 240         //主设备号
               
static int led_open(struct inode *inode, struct file *filp)       
    {      
      unsigned int tmp;      
      tmp = readl(S3C64XX_GPMCON);       
      tmp = (tmp & ~(0xffff)) | (0x1111); //set the GPIO output mode       
      writel(tmp, S3C64XX_GPMCON);
      tmp = readl(S3C64XX_GPMDAT);      //读取寄存器之前的数据
      tmp |= 0xf;
      writel(tmp, S3C64XX_GPMDAT);      //写入1让所有的灯都熄灭   
      printk("$$$$$$$$$$$led_open$$$$$$$$$\n");       
      return 0;      
    }

//此函数为空,我们在此不涉及设备的读操作         
static ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)      
    {      
      printk("$$$$$$$$$$led_read$$$$$$$$$\n");      
      return count;       
    }
      
static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)       
    {
      char mbuf;      
      unsigned int tmp;      
      copy_from_user(&mbuf,buf,count); //将用户空间的数据复制到内核空间的mbuf
      tmp = readl(S3C64XX_GPMDAT);   //获取GPMDAT寄存器数据                   
      switch(mbuf)                     //通过switch语句解析用户空间传下来的命令,然后case语句中对控制设备实现对应的操作   
          {      
            case 0:   tmp |= (0x1);//GPM0输出高电平,led1灭       
                        break;      
            case 1:   tmp &= ~(0x1); //GPM0输出低电平,led1亮
                        break;      
            case 2:   tmp |= (0x2);//GPM1输出高电平,led2灭
                        break;
            case 3:   tmp &= ~(0x2); //GPM1输出低电平,led2亮
                        break;   
            case 4:   tmp |= (0x4);//GPM2输出高电平,led3灭
                        break;
            case 5:   tmp &= ~(0x4); //GPM2输出低电平,led3亮
                        break;
            case 6:   tmp |= (0x8);//GPM3输出高电平,led4灭  
                        break;
            case 7:   tmp &= ~(0x8); //GPM3输出低电平,led4亮  
                        break;
            default:   
                        break;   
      }
      writel(tmp, S3C64XX_GPMDAT);   //将新数据写入GPMDAT         
      printk("$$$$$$$$$$led_write$$$$$$$$$\n");      
      return count;      
    }
                  
static int led_release(struct inode *inode, struct file *filp)       
    {   
      printk("$$$$$$$$$$led_release$$$$$$$$$\n");      
      return 0;      
    }
      
/*这个结构是字符设备驱动的核心,当应用程序操作设备文件所提供的
   open,read,write等函数,最终会调用到这个结构中的对应函数*/    
static struct file_operations my_fops = {      
      .owner   = THIS_MODULE,//这是一个宏,指向编译模块时自动创建的__this_module变量    
      .open    = led_open,
      .read    = led_read,      
      .write   = led_write,   
      .release = led_release,   
    };
                  
static int led_init(void)      
    {       
      int rc;    
      printk("Test led dev\n");       
      rc = register_chrdev(LED_MAJOR, DEVICE_NAME, &my_fops);   //注册设备驱动到内核,成功返回0

      /*注册失败时函数返回负整数,在此判断是否注册失败,然后对失败后进行相应的处理,好的驱动
          程序应该考虑到若失败,若何妥善处理,以免造成内核崩溃等错误后果造成大的隐患*/
      if(rc < 0)   
          {      
             printk("register %s mychar dev error\n", DEVICE_NAME);
             return -1;       
          }      
      printk("$$$$$$$$$ register led dev OK\n");    
      return 0;       
    }
      
       
static void led_exit(void)      
    {      
      unsigned int tmp;
      tmp = readl(S3C64XX_GPMCON);         //获取GPMCON寄存器数据
      tmp &= (~0xffff);                  //清除之前设置
      writel(tmp, S3C64XX_GPMCON);         //配置IO模式 

      /*注销函数中的参数,要与注册的参数保持相同,因为只有这样才能注册什么就注销什么,不会
          造成错误注销以免导致其他设备不能用,若写入的参数在内核中并不存在,注销是会失败的*/
      unregister_chrdev(LED_MAJOR, DEVICE_NAME); 
      printk("Good Bye!\n");      
    }
                   
    //动态加载驱动接口(必需)      
    module_init(led_init);      
    module_exit(led_exit); 
    //其它信息(非必需)
    MODULE_AUTHOR("xy02261110@sina.com");               //驱动程序作者信息
    MODULE_DESCRIPTION("OK6410(S3C6410) LED Driver");   //一些描述信息 
    MODULE_LICENSE("GPL");                              //遵循的原则


接着是makefile
obj-m := led.o
<span style="font-family:Times New Roman;font-size:16px;"> </span>


然后建个makemod
make -C /forlinx/linux-3.0.1/ M=`pwd` modules

led.c,makefile,makemod放在同一个目录下,比如led_drv。通过终端进入这个目录,然后只要source makemod就可以编译成led.ko。



从图中可以看到已经生成led.ko。然后到led_drv目录下去看,果然已经有了。


为了实现驱动还要写应用程序,应用程序名为ledapp.c,代码如下:
#include <stdio.h>      
#include <sys/types.h>       
#include <sys/stat.h>       
#include <fcntl.h> 
       
#define DEVICE "/dev/myled"
       
int main(void)      
    {   
      int fd, i;
      const unsigned char buf[i] = {0, 1, 2, 3, 4, 5, 6, 7};   
      fd = open(DEVICE, O_RDWR);
      if(fd < 0)
      {
            printf("Open /dev/myled file error\n");
            return -1;   
      }         
      while(1)
      {
            for(i = 7; i>=0; i--)
             {
                write(fd, &buf[i], 1);
                sleep(1);
             }
      }   
      close(fd);    
      return 0;      
    }

应用程序比较简单,就是一个简单的流水灯,没有写注释。

然后makefile
CC = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-gcc    
ledapp:ledapp.o   
        $(CC) -o ledapp ledapp.o   
ledapp.o:ledapp.c   
        $(CC) -c ledapp.c    
clean :   
        rm ledapp.o

把应用程序ledapp.c和makefile放在一个目录下,比如led_app下,然后从终端中进入这一个目录,执行make就可以了。
从下图中可以看到已经生成了ledapp,到led_app目录中,看到果然已经有ledapp了。


通过sd卡可以把编译好的led.ko和ledapp放到板子上,当然还有别的办法。
先在板子的终端中输入cd sdcard进入sd卡的目录,如果led.ko和ledapp就放在sd卡的根目录下,那这样就可以了。如果放在更下层的目录,那就还要继续进入下层目录。
然后在终端中输入insmodled.ko,如果注册没有问题
继续输入 mknod/dev/myledc   2400       
设备文件也创立以后就可以跑应用了,直接输入ledapp,就可以看到4个led成流水灯跑起来了。

0 0
原创粉丝点击