Linux设备驱动之plat_led驱动测试

来源:互联网 发布:gta5捏脸数据女韩国 编辑:程序博客网 时间:2024/05/21 12:02

通过前面对platform总线驱动的介绍以及基于platform总线编写的设备驱动代码分析,进一步加深了对platform总线的认识。现在基于驱动代码编写简单驱动测试程序验证驱动代码的正确性。

1.驱动文件


这几个就是程序的源码及所需头文件。

(1)plat_led.c

/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  plat_led.c *    Description:  This file  *                  *        Version:  1.0.0(2017年04月15日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年04月15日 13时32分10秒" *                  ********************************************************************************/#include "s3c_driver.h"     #define DRV_AUTHOR                "Guo Wenxue <guowenxue@gmail.com>"  #define DRV_DESC                  "S3C24XX LED driver"     /* Driver version*/  #define DRV_MAJOR_VER             1  #define DRV_MINOR_VER             0  #define DRV_REVER_VER             0     #define DEV_NAME                  DEV_LED_NAME     //#define DEV_MAJOR                 DEV_LED_MAJOR  #ifndef DEV_MAJOR  #define DEV_MAJOR                 0 /*  dynamic major by default */  #endif     #define TIMER_TIMEOUT             40     static int debug = DISABLE;  static int dev_major = DEV_MAJOR;  static int dev_minor = 0;        /* ============================ Platform Device part ===============================*/  /*  LED hardware informtation structure*/  struct s3c_led_info   //LED结构体的定义  {      unsigned char           num;              /* The LED number  */      unsigned int            gpio;             /* Which GPIO the LED used */      unsigned char           active_level;     /* The GPIO pin level(HIGHLEVEL or LOWLEVEL) to turn on or off  */      unsigned char           status;           /* Current LED status: OFF/ON */      unsigned char           blink;            /* Blink or not */  };     /*  The LED platform device private data structure */  struct s3c_led_platform_data  //总线的定义  {      struct s3c_led_info    *leds;      int                     nleds;  };        /*  LED hardware informtation data*/  static struct s3c_led_info  s3c_leds[] = { //LED灯的信息      [0] = {          .num = 1,          .gpio = S3C2410_GPB(5),          .active_level = LOWLEVEL,          .status = OFF,                                                                                                                    .blink = ENABLE,      },      [1] = {          .num = 2,          .gpio = S3C2410_GPB(6),          .active_level = LOWLEVEL,          .status = OFF,          .blink = DISABLE,      },      [2] = {          .num = 3,          .gpio = S3C2410_GPB(8),          .active_level = LOWLEVEL,          .status = OFF,          .blink = DISABLE,      },      [3] = {          .num = 4,          .gpio = S3C2410_GPB(10),          .active_level = LOWLEVEL,          .status = OFF,          .blink = DISABLE,      },  };     /*  The LED platform device private data */  static struct s3c_led_platform_data s3c_led_data = {//定义LED灯的结构体信息      .leds = s3c_leds,//LED灯的每条信息      .nleds = ARRAY_SIZE(s3c_leds),//灯数  };     struct led_device//定义led_device的结构体  {      struct s3c_led_platform_data    *data;      struct cdev                     cdev;      struct class                    *dev_class;      struct timer_list               blink_timer;  } led_device;     static void platform_led_release(struct device * dev)//撤销LED灯的函数  {      int i;      struct s3c_led_platform_data *pdata = dev->platform_data;    dbg_print("%s():%d\n", __FUNCTION__,__LINE__);         /* Turn all LED off */      for(i=0; i<pdata->nleds; i++)      {           s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);      }  }     static struct platform_device s3c_led_device = {//定义设备结构体      .name    = "s3c_led",      .id      = 1,      .dev     =      {          .platform_data = &s3c_led_data,          .release = platform_led_release,      },  };           /* ===================== led device driver part ===========================*/     void led_timer_handler(unsigned long data) {      int  i;      struct s3c_led_platform_data *pdata = (struct s3c_led_platform_data *)data;         for(i=0; i<pdata->nleds; i++)      {          if(ON == pdata->leds[i].status)//判断LED灯的亮灭          {                s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);          }          else          {                s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);          }                                                     if(ENABLE == pdata->leds[i].blink )  /* LED should blink */                    {              /* Switch status between 0 and 1 to turn LED ON or off */              pdata->leds[i].status = pdata->leds[i].status ^ 0x01;          }             mod_timer(&(led_device.blink_timer), jiffies + TIMER_TIMEOUT);//计时器更新,jiffies是全局变量,在头文件      }  }        static int led_open(struct inode *inode, struct file *file)//LED开启函数  {      struct led_device *pdev ;      struct s3c_led_platform_data *pdata;    /*container_of 在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针, 就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址*/      pdev = container_of(inode->i_cdev,struct led_device, cdev);      pdata = pdev->data;         file->private_data = pdata;         return 0;  }        static int led_release(struct inode *inode, struct file *file)//LED关闭函数{      return 0;  }     static void print_led_help(void)//帮助信息  {      printk("Follow is the ioctl() command for LED driver:\n");      printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);      printk("Get Driver verion  command : %u\n", GET_DRV_VER);      printk("Turn LED on command        : %u\n", LED_ON);      printk("Turn LED off command       : %u\n", LED_OFF);      printk("Turn LED blink command     : %u\n", LED_BLINK);  }     /* compatible with kernel version >=2.6.38*/  static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  {      struct s3c_led_platform_data *pdata = file->private_data;         switch (cmd)  //cmd为内核空间传过来的命令    {          case SET_DRV_DEBUG:              dbg_print("%s driver debug now.\n", DISABLE == arg ? "Disable" : "Enable");              debug = (0==arg) ? DISABLE : ENABLE;              break;          case GET_DRV_VER:              print_version(DRV_VERSION);              return DRV_VERSION;     /*如果传入LED_OFF命令,则关闭LED灯*/        case LED_OFF:              if(pdata->nleds <= arg)              {                 printk("LED%ld doesn't exist\n", arg);                 return -ENOTTY;              }              pdata->leds[arg].status = OFF;              pdata->leds[arg].blink = DISABLE;              break;     /*如果传入LED_OFF命令,则开启LED灯*/        case LED_ON:              if(pdata->nleds <= arg)              {                 printk("LED%ld doesn't exist\n", arg);                 return -ENOTTY;              }              pdata->leds[arg].status = ON;              pdata->leds[arg].blink = DISABLE;              break;     /*如果传入LED_BLINK命令,则闪烁LED灯*/        case LED_BLINK:              if(pdata->nleds <= arg)              {                 printk("LED%ld doesn't exist\n", arg);                 return -ENOTTY;              }              pdata->leds[arg].blink = ENABLE;               pdata->leds[arg].status = ON;              break;             default:              dbg_print("%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);              print_led_help();              return -EINVAL;         }      return 0;  }        static struct file_operations led_fops = {      .owner = THIS_MODULE,      .open = led_open,      .release = led_release,      .unlocked_ioctl = led_ioctl, /* compatible with kernel version >=2.6.38*/  };        static int s3c_led_probe(struct platform_device *dev)//设备和驱动相互识别是调用从由内核把platform_device传到probe函数  {      struct s3c_led_platform_data *pdata = dev->dev.platform_data;      int result = 0;      int i;      dev_t devno;         /* Initialize the LED status */      for(i=0; i<pdata->nleds; i++)      {           s3c2410_gpio_cfgpin(pdata->leds[i].gpio, S3C2410_GPIO_OUTPUT);  //设置相应gpio为输出模式         if(ON == pdata->leds[i].status)           {              s3c2410_gpio_setpin(pdata->leds[i].gpio, pdata->leds[i].active_level);  //LED管脚亮         }           Else          {              s3c2410_gpio_setpin(pdata->leds[i].gpio, ~pdata->leds[i].active_level);  //LED管脚灭          }      }         /*  Alloc the device for driver */  if (0 != dev_major)  //如果已经有主设备号 {          devno = MKDEV(dev_major, dev_minor);  //生成devno值        result = register_chrdev_region(devno, 1, DEV_NAME);  //分配次设备号    }      else      {         result = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME);  //动态分配主次设备号         dev_major = MAJOR(devno);      }         /* Alloc for device major failure */      if (result < 0)      {         printk("%s driver can't get major %d\n", DEV_NAME, dev_major);  //分配失败打印信息         return result;      }         /* Initialize button structure and register cdev*/      memset(&led_device, 0, sizeof(led_device));  //清空led_device结构体空间    led_device.data = dev->dev.platform_data;  //获取平台数据    cdev_init (&(led_device.cdev), &led_fops);  //注册cdev结构体    led_device.cdev.owner  = THIS_MODULE;         result = cdev_add (&(led_device.cdev), devno , 1);  //添加设备到内核    if (result)      {          printk (KERN_NOTICE "error %d add %s device", result, DEV_NAME);          goto ERROR;      }         led_device.dev_class = class_create(THIS_MODULE, DEV_NAME);  //为设备自动创建节点    if(IS_ERR(led_device.dev_class))      {          printk("%s driver create class failture\n",DEV_NAME);          result =  -ENOMEM;          goto ERROR;      }     #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)      device_create(led_device.dev_class, NULL, devno, NULL, DEV_NAME);  #else      device_create (led_device.dev_class, NULL, devno, DEV_NAME);  #endif            /*  Initial the LED blink timer */  /*启用内核定时器,初始 timer_list 的结构的一些变量*/    init_timer(&(led_device.blink_timer));      led_device.blink_timer.function = led_timer_handler;     led_device.blink_timer.data = (unsigned long)pdata;    led_device.blink_timer.expires  = jiffies + TIMER_TIMEOUT;//闪烁时间间隔      add_timer(&(led_device.blink_timer));  //将blink_timer 加入内核timer列表中,等待处理       printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER);         return 0;        ERROR:      printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER);      cdev_del(&(led_device.cdev));         unregister_chrdev_region(devno, 1);      return result;     }     static int s3c_led_remove(struct platform_device *dev)//释放主次设备号给Linux内核  {      dev_t devno = MKDEV(dev_major, dev_minor);         del_timer(&(led_device.blink_timer));         cdev_del(&(led_device.cdev));      device_destroy(led_device.dev_class, devno);      class_destroy(led_device.dev_class);         unregister_chrdev_region(devno, 1);      printk("S3C %s driver removed\n", DEV_NAME);         return 0;  }        static struct platform_driver s3c_led_driver = {//定义总线驱动结构体      .probe      = s3c_led_probe,    .remove     = s3c_led_remove,      .driver     = {            .name       = "s3c_led",          .owner      = THIS_MODULE,      },  };        static int __init s3c_led_init(void)//LED初始化  {     int       ret = 0;     /*设备注册:把指定设备添加到内核中平台总线的设备列表,等待匹配,匹配成功则回调驱动中probe函数;*/   ret = platform_device_register(&s3c_led_device);     if(ret)     {          printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__,__LINE__, ret);          goto fail_reg_plat_dev;     }     dbg_print("Regist S3C LED Platform Device successfully.\n");        /*驱动注册:把指定驱动添加到内核中平台总线的驱动列表,等待匹配,匹配成功则回调驱动中probe;*/   ret = platform_driver_register(&s3c_led_driver);     if(ret)     {          printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__,__LINE__, ret);          goto fail_reg_plat_drv;     }     dbg_print("Regist S3C LED Platform Driver successfully.\n");        return 0;     fail_reg_plat_drv:     platform_driver_unregister(&s3c_led_driver);  fail_reg_plat_dev:     return ret;  }        static void s3c_led_exit(void)//LED撤销  {      dbg_print("%s():%d remove LED platform drvier\n", __FUNCTION__,__LINE__);      platform_driver_unregister(&s3c_led_driver);      dbg_print("%s():%d remove LED platform device\n", __FUNCTION__,__LINE__);      platform_device_unregister(&s3c_led_device);  }     module_init(s3c_led_init);  module_exit(s3c_led_exit);     module_param(debug, int, S_IRUGO);  module_param(dev_major, int, S_IRUGO);  module_param(dev_minor, int, S_IRUGO);     MODULE_AUTHOR(DRV_AUTHOR);  MODULE_DESCRIPTION(DRV_DESC);  MODULE_LICENSE("GPL");  MODULE_ALIAS("platform:S3C24XX_led");  

(2)s3c_driver.h

/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  s3c_driver.h *    Description:  This file  *                  *        Version:  1.0.0(2017年04月15日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年04月15日 13时32分10秒" *                  ********************************************************************************/#ifndef __S3C_DRIVER_H#define __S3C_DRIVER_H#include <linux/version.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kref.h>#include <linux/spinlock.h>#include <asm/uaccess.h>#include <linux/mutex.h>#include <linux/delay.h>#include <linux/fs.h>#include <linux/device.h>#include <linux/i2c.h>#include <linux/string.h>#include <linux/bcd.h>#include <linux/miscdevice.h>#include <linux/poll.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/sysfs.h>#include <linux/proc_fs.h>#include <linux/rtc.h>#include <linux/spinlock.h>#include <linux/usb.h>#include <asm/uaccess.h>#include <asm/delay.h>#include <linux/syscalls.h>  /* For sys_access*/#include <linux/platform_device.h>#include <linux/unistd.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/irq.h>#include <mach/regs-gpio.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)#include <mach/hardware.h>#include <mach/gpio.h>#include <asm/irq.h>#else #include <asm-arm/irq.h>#include <asm/arch/gpio.h>#include <asm/arch/hardware.h>#endif#include "plat_ioctl.h"/* =========================================================================== *         S3C24XX device driver common macro definition  *===========================================================================*/#define ENPULLUP                    1#define DISPULLUP                   0#define HIGHLEVEL                   1#define LOWLEVEL                    0#define INPUT                       1#define OUTPUT                      0#define OFF                         0#define ON                          1#define ENABLE                      1#define DISABLE                     0#define TRUE                        1#define FALSE                       0/* =========================================================================== *         S3C24XX device driver name and Major number define  *===========================================================================*/#define DEV_LED_NAME                "led"#define DEV_LED_MAJOR               203#define DEV_BUTTON_NAME             "button"#define DEV_BUTTON_MAJOR            "211"#define DEV_ADC_NAME                "adc"#define DEV_ADC_MAJOR               "212"/*  ***** Bit Operate Define *****/#define SET_BIT(data, i)   ((data) |=  (1 << (i)))    /*   Set the bit "i" in "data" to 1  */#define CLR_BIT(data, i)   ((data) &= ~(1 << (i)))    /*   Clear the bit "i" in "data" to 0 */#define NOT_BIT(data, i)   ((data) ^=  (1 << (i)))    /*   Inverse the bit "i" in "data"  */#define GET_BIT(data, i)   ((data) >> (i) & 1)        /*   Get the value of bit "i"  in "data" */#define L_SHIFT(data, i)   ((data) << (i))            /*   Shift "data" left for "i" bit  */#define R_SHIFT(data, i)   ((data) >> (i))            /*   Shift "data" Right for "i" bit  *//* =========================================================================== *         S3C24XX device driver common function definition  *===========================================================================*/#define SLEEP(x)    {DECLARE_WAIT_QUEUE_HEAD (stSleep); if (10 > x) mdelay ((x * 1000)); \                        else wait_event_interruptible_timeout (stSleep, 0, (x / 10));}#define VERSION_CODE(a,b,c)       ( ((a)<<16) + ((b)<<8) + (c))#define DRV_VERSION               VERSION_CODE(DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER)#define MAJOR_VER(a)              ((a)>>16&0xFF)#define MINOR_VER(a)              ((a)>>8&0xFF)#define REVER_VER(a)              ((a)&0xFF)#define dbg_print(format,args...) if(DISABLE!=debug) \        {printk("[kernel] ");printk(format, ##args);}    static inline void print_version(int version){#ifdef __KERNEL__        printk("%d.%d.%d\n", MAJOR_VER(version), MINOR_VER(version), REVER_VER(version));#else        printf("%d.%d.%d\n", MAJOR_VER(version), MINOR_VER(version), REVER_VER(version));#endif}#endif /* __S3C_DRIVER_H */
(3)plat_ioctl.h
/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  s3c_driver.h *    Description:  This file  *                  *        Version:  1.0.0(2017年04月15日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年04月15日 13时32分10秒" *                  ********************************************************************************/#ifndef __PLAT_IOCTL_H#define __PLAT_IOCTL_H#include <asm/ioctl.h>/*=========================================================================== *                Common ioctl command definition  *===========================================================================*/#define PLATDRV_MAGIC           0x60/*=========================================================================== *                 ioctl command for all the drivers 0x01~0x0F *===========================================================================*//*args is enable or disable*/#define SET_DRV_DEBUG               _IO (PLATDRV_MAGIC, 0x01)#define GET_DRV_VER                 _IO (PLATDRV_MAGIC, 0x02)/*=========================================================================== *                 ioctl command for few ioctl() cmd driver 0x10~0x2F *===========================================================================*//* LED driver */#define LED_OFF                     _IO (PLATDRV_MAGIC, 0x18)#define LED_ON                      _IO (PLATDRV_MAGIC, 0x19)#define LED_BLINK                   _IO (PLATDRV_MAGIC, 0x1A)#define ADC_SET_CHANNEL             _IO (PLATDRV_MAGIC, 0x1B)/*=========================================================================== *                   ioctl command for GPRS driver 0x30~0x4F *===========================================================================*/#define GPRS_POWERDOWN              _IO (PLATDRV_MAGIC, 0x30)#define GPRS_POWERON                _IO (PLATDRV_MAGIC, 0x31)#define GPRS_RESET                  _IO (PLATDRV_MAGIC, 0x32)#define GPRS_POWERMON               _IO (PLATDRV_MAGIC, 0x33)#define GPRS_CHK_SIMDOOR            _IO (PLATDRV_MAGIC, 0x36)#define GPRS_SET_DTR                _IO (PLATDRV_MAGIC, 0x37)#define GPRS_SET_RTS                _IO (PLATDRV_MAGIC, 0x38)#define GPRS_GET_RING               _IO (PLATDRV_MAGIC, 0x39)#define SET_PWUP_TIME               _IO (PLATDRV_MAGIC, 0x3A)#define SET_PWDOWN_TIME             _IO (PLATDRV_MAGIC, 0x3B)#define SET_RESET_TIME              _IO (PLATDRV_MAGIC, 0x3C)#define GPRS_CHK_MODEL              _IO (PLATDRV_MAGIC, 0x3E) /*=========================================================================== *                   ioctl command for EEPROM driver 0x50~0x5F *===========================================================================*/#define LL_POWEROFF                 _IO (PLATDRV_MAGIC, 0x50)#define LL_POWERON                  _IO (PLATDRV_MAGIC, 0x51)#define LL_STOP                     _IO (PLATDRV_MAGIC, 0x52)#define LL_START                    _IO (PLATDRV_MAGIC, 0x53)#define LL_READ                     _IO (PLATDRV_MAGIC, 0x54)#define LL_WRITE                    _IO (PLATDRV_MAGIC, 0x55)#define LL_DATALOW                  _IO (PLATDRV_MAGIC, 0x56)#define LL_ACKNAK                   _IO (PLATDRV_MAGIC, 0x57)#endif                          /* __PLAT_IOCTL_H */
2.Makefile文件

[weiming@Huangweiming plat_led]$ vim Makefile 


make之后会生成我们所需要的.ko的驱动模块

3.测试文件
[[weiming@Huangweiming plat_led]$ vim test_plat_led.c

/********************************************************************************* *      Copyright:  (C) 2017 Huang Weiming<710564672@qq.com> *                  All rights reserved. * *       Filename:  test_plat_led.c *    Description:  This file  *                  *        Version:  1.0.0(2017年04月20日) *         Author:  Huang Weiming <710564672@qq.com> *      ChangeLog:  1, Release initial version on "2017年04月20日 16时42分40秒" *                  ********************************************************************************/#include #include #include #include #include #include #include #define DEVNAME_LEN    10#define PLATDRV_MAGIC             0x60#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)#define LED_BLINK               _IO (PLATDRV_MAGIC, 0x1A)int main(int argc, char **argv){        int  ctl_number;        int  led_number;        int  fd;        char dev_name[DEVNAME_LEN] = {0,0,0,0};        if(argc>2)        {            snprintf(dev_name,sizeof(dev_name),"/dev/led");            fd = open(dev_name,O_RDWR,0755);            ctl_number = atoi(argv[1]);            led_number = atoi(argv[2]);            if(0 == ctl_number)                ioctl(fd,LED_ON,led_number);            else if(1 == ctl_number)                ioctl(fd,LED_OFF,led_number);            else if(2 == ctl_number)                ioctl(fd,LED_BLINK,led_number);            else            {                printf("error!\n");                printf("Plesae enter the correct parameters.\n");                printf("correct format: %s 0(on)|1(off)|2(blink) 0|1|2|3\n",argv[0]);            }        }}
编译成功后生成a.out可执行程序。

4.开发板烧录
通过tftp命令把文件上传到开发板


接着进行模块加载测试


通过执行./a.out可看到开发板对应的灯发生变化,测试程序执行成功!










原创粉丝点击