铅笔

在你的害怕中坚持的越多,你就会越自信
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

led驱动

Posted on 2017-01-03 19:19  黑色の铅笔  阅读(374)  评论(0编辑  收藏  举报

驱动步骤:

      1、驱动框架:一般读驱动代码需要module_init一层层找代码

    


      2、硬件配置

  代码中led_ioctl函数设置引脚的电平高低,该函数是驱动程序对设备的通道进行统一设置/控制的函数 

   一、  在用户空间,使用ioctl系统调用来控制设备,原型如下:

    int ioctl(int fd,unsigned long cmd,...);
  fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情
二、驱动ioctl方法
int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。
*/
在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的。关键在于怎么样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径。

  用户使用  int ioctl(int fd,unsinged long cmd,...)  时,...就是要传递的参数;
  再通过  int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long  arg)  中的arg传递;不同颜色代表对应的参数

现阶段能够理解成每一个用户程序的ioctl对应其内核中的一个ioctl函数,且参数需要用户层传递给驱动层 (该例中) 

代码很简单 :

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define DEVICE_NAME "myled"   /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
static struct class *leds_class;
static struct class *led_dev_class;
int major;


/* 用来指定LED所用的GPIO引脚 */
static unsigned long led_table[]={
    
    S3C2410_GPF5,
    S3C2410_GPF6,
    
};

/* 用来指定GPIO引脚的功能:输出 */
static unsigned int led_cfg_table [] = {
    
    S3C2410_GPF5_OUTP,
    S3C2410_GPF6_OUTP,
};

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define LED_ON    0
#define LED_OFF   1
/* 应用程序对设备文件/dev/leds执行open(...)时,
 * 就会调用s3c24xx_leds_open函数
 */
static int led_open (struct inode *inode, struct file *filep)  
{  
    int i;
    // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
     for (i = 0; i <2; i++) {
        // 设置GPIO引脚的功能:本驱动中LED所涉及的GPIO引脚设为输出功能
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
    }
    return 0;  
}  
static int led_ioctl(
    struct inode *inode, 
    struct file *file, 
    unsigned int cmd, 
    unsigned long arg)
{
    
    if (arg > 2) {
        return -EINVAL;
    }
     switch(cmd) {
         case LED_ON:
        // 设置指定引脚的输出电平为0
        s3c2410_gpio_setpin(led_table[arg], 0);
        return 0;
        case LED_OFF:
        // 设置指定引脚的输出电平为1
        s3c2410_gpio_setpin(led_table[arg], 1);
        return 0;
          default:
        return -EINVAL;
    } 
}
/* 这个结构是字符设备驱动程序的核心
 * 当应用程序操作设备文件时所调用的open、read、write等函数,
 * 最终会调用这个结构中指定的对应函数
 */
static struct file_operations led_ops=  
{    
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   = led_open,  
    .ioctl  = led_ioctl ,
    
    
}; 
 
/*
 * 执行insmod命令时就会调用这个函数 
 */
  
static int led_init(void)  
{  
    int ret;
    /* 注册字符设备
     * 参数为主设备号、设备名字、file_operations结构;
     * 这样,主设备号就和具体的file_operations结构联系起来了,
     * 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
     * LED_MAJOR可以设为0,表示由内核自动分配主设备号
     */
     major = register_chrdev(0, DEVICE_NAME, &led_ops);
      if (major < 0) 
      {
      printk(DEVICE_NAME  " can't register major number number::%d\n",major);
      return ret;
      }
    printk(DEVICE_NAME " initialized1\n");
    leds_class = class_create(THIS_MODULE, "leds");
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);
    led_dev_class = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "perled"); /* /dev/leds */ 
    return 0;

}

/*
 * 执行rmmod命令时就会调用这个函数 
 */
static void led_exit(void)
{
    class_device_unregister(led_dev_class, MKDEV(major, 0));
    class_destroy(leds_class);       
     /* 卸载驱动程序 */
    unregister_chrdev(major, DEVICE_NAME);
  
}






module_init(led_init);  
module_exit(led_exit);  


MODULE_AUTHOR("http://www.100ask.net");
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
MODULE_LICENSE("GPL");  

 make

insmod XXX.ko

测试代码(应用程序代码):

#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <stdio.h>  
#include <sys/ioctl.h>
#include <stdlib.h>
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */
#define IOCTL_LED_ON    0
#define IOCTL_LED_OFF   1
/*
  *  ledtest <dev> <on|off>
  */

void print_usage(char *file)
{
    printf("Usage:\n");
    printf("./a.out 1 on\n");
}


int main(int argc,char**argv)  
{  
    int fd;  
    int ret;
    int led_NO;
    char val;    
    if(argc!=3)
    {
        
        printf("error USAGE\n");
        exit(1);
    }
  //1、打开设备
    fd = open("/dev/perled",O_RDWR);  
    led_NO=strtoul(argv[1],0,0)-1;//确定LED
    if(fd<0)  
    {  
        perror("open fail \n");  
        return -1;  
    }  
   if (!strcmp("on", argv[2]))
    {
        // 亮灯
       ioctl(fd,IOCTL_LED_ON,led_NO);// 设备 亮灭 那个led、
    }
    else if (!strcmp("off", argv[2]))
    {
        // 灭灯
        ioctl(fd,IOCTL_LED_OFF,led_NO);
    }
    else
    {
        print_usage(argv[0]);
        return 0;
    }
    
  
    close(fd);
    return 0;
}  
/// //   // / //./a.out 1 on

 驱动程序中的ioctl也可以用write函数但是需要用到copy_from_user

将数据由其用户 空间上传到内核空间了

static ssize_t led_write(struct file *file, const char __user *data,
                  size_t len, loff_t * ppos)
{
    int val;
    copy_from_user(&val, data, 1); //    copy_to_user();
    s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1));//避免使用if语句   
    
    return 1;
                      
}

 

 

ioctl:http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html