Ti437x LED总线驱动模型程序+设备树

来源:互联网 发布:java modbus与rs485 编辑:程序博客网 时间:2024/06/11 22:49

本文主要记录AM437X驱动的LED。含简单的字符设备驱动、设备驱动模型、设备树以及LED子系统。


目前就Linux驱动的理解是:

Linux驱动 = 裸机 + 框架

关于框架,目前的理解是:

以LED驱动为例,之前印象中就是韦老大的思路,现在init里注册、硬件初始化,然后应用层open()、read()就调用了file_operations里面的drv_open()、drv_write()等,算是最简单的驱动框架。
然后韦老大又提及了总线设备驱动模型,将设备和驱动分离,感受到了新的框架。不久前,简单接触了下设备树,感觉就是总线设备驱动模型的修改(升级),将原来的设备部分,不再单独放在代码里,而是放在dts里面,开机加载,然后驱动匹配获取硬件资源。因此,感觉驱动的框架在一步一步的发展,优化,最原始的注册、open等框架,还是不变。
同时,了解到了除输入子系统的其它子系统,加深了对这一模式的理解。感觉就是,将某个硬件资源无缝的融入现有的环境中,而无须改变应用层的程序。

这就是目前的一点小小理解吧,算是打开了个入口,希望以后了解得更加全面、细致。

1.搭建开发环境

1.1安装TI_SDK

先在TI官网下载ti-processor-sdk-linux-am437x-evm-01.00.00.03-Linux-x86-Install.bin
在Ubuntu(only Ubuntu 12.04 LTS and Ubuntu 14.04 LTS are supported)下,对该文件加入可执行权限,然后直接运行。安装目录选择默认即可。完成之后,便在当前用户的home目录生成了所有所需文件。

1.2编译内核

在当前生成ti-processor-sdk-linux-am437x-evm-01.00.00.03目录下,有个Makefile,打开后可以看到相关的编译选项,如:

  • 编译全部文件:make all
  • 编译内核:make linux
  • 编译u-boot:make u-boot-spl
  • 以及make的依赖:-include Rules.make。在本层目录里,打开Rules.make,可以知道内核的默认配置文件:
#defconfigDEFCONFIG=tisdk_am437x-evm_defconfig

通过查找,tisdk_am437x-evm_defconfig在~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/configs里。
这里通过修改该配置文件,然后重新编译内核,即可关闭系统LED相关的驱动,在后面自己写LED驱动时,防止互相干扰。
因此将tisdk_am437x-evm_defconfig配置文件里的所有有关LED的配置都关闭掉。

最后在顶层目录执行make linux,编译完成后,生成~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/boot/zImage文件。

1.3烧写SD卡

回到~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/bin下,TI制作了很多脚本,其中的create-sdcard.sh就是制作SD卡的。Ubuntu插上SD卡,然后切换成root用户,执行该脚本,根据提示一路选择下去即可。

这里烧写完了,测试发现并没有使用之前编译的内核,分析脚本后发现,该脚本直接使用的~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/filesystem下的tisdk-rootfs-image-am437x-evm.tar.gz。脚本将该文件作为根文件系统放入SD卡,因此并没有使用之前编译的内核。解决方法要么在执行脚本的过程中根据提示输入相关的路径,要么在制作好SD卡后,将编译好的内核覆盖掉SD卡的内核即可。我选择的后者:cp ~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/boot/zImage /media/hceng/rootfs/boot/

最后将制作好的SD卡插上开发板启动即可。

2.简单的字符驱动

先记录下几个重要类型或结构体:

  • 表示设备号(32位机中:高12位表示主设备号,低20位表示次设备号)
typedef __kernel_dev_t  dev_t; 
  • 描述字符设备
struct cdev {    struct kobject kobj; //内嵌kobject结构体,用于设备驱动模型管理    struct module *owner; //包含指向该结构的模块的指针,用于引用计数    const struct file_operations *ops; //指向字符设备操作函数集的指针    struct list_head list; //该结构将使用该驱动的字符设备连接成一个链表    dev_t dev; //该字符设备的其实设备号,一个设备可能有多个设备号    unsigned int count; //使用该字符设备驱动的设备数量};
  • 描述类
struct class{    const char *name; //类名称    struct module *owner; //对应模块    struct subsystem subsys; //对应的subsystem;    struct list_head children; //class_device链表    struct list_head  interfaces; //class_interface链表    struct semaphore  sem; /用于同步的信号锁    struct class_attribute *class_attrs; //类属性    int (*uevent)(struct class_device *dev,char **envp,int num_envp,                  char *buffer,int buffer_size); //事件    void (*release)(struct class_device *dev); //释放类设备    void (*class_release)(struct class *class); //释放类}

总结下,目前理解的字符设备编写流程:

1)驱动加载函数:xx_drv_init()
  1.1)申请设备号:alloc_chrdev_region()
  1.2)cde初始化(绑定fops):cdev_init()
  1.3)注册到内核:cdev_add()
  1.4)创建类:class_create()
  1.5)向类中添加设备(mdev自动创建设备节点):device_create()
  1.6)硬件相关(内存映射):ioremap()
2)驱动卸载函数:xx_drv_exit()
  2.1)移除设备:device_destroy()
  2.2)移除类:class_destroy()
  2.3)注销cdev:cdev_del()
  2.4)释放设备号:unregister_chrdev()
  2.5)释放内存:iounmap()
3)必要修饰:module_init(xx_drv_init);module_exit(xx_drv_exit);MODULE_LICENSE("GPL");
4)构造file_operations:struct file_operations xx_drv_fops;
5)实现file_operations里每个函数:xx_open()、xx_write()……

2.1驱动代码

{[leds_drv.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v1.0/leds_drv.c %}

#include <linux/kernel.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/device.h>#include <linux/cdev.h>#define TI_LEDS_CNT     4int major;static struct cdev leds_cdev;static struct class *leds_cls;static volatile unsigned long *PRCM_CM_PER_GPIO5_CLKCTRL = NULL;  static volatile unsigned long *CTRL_CONF_UART3_RXD = NULL;  static volatile unsigned long *CTRL_CONF_UART3_TXD = NULL;  static volatile unsigned long *CTRL_CONF_UART3_CTSN = NULL;static volatile unsigned long *CTRL_CONF_UART3_RTSN = NULL;static volatile unsigned long *GPIO_OE = NULL;static volatile unsigned long *GPIO_SETDATAOUT = NULL;static volatile unsigned long *GPIO_DATAOUT = NULL; static int leds_drv_open(struct inode *inode, struct file *file)  {        int minor = iminor(file->f_inode);    printk(KERN_INFO"%s OK.\n",__func__);    *PRCM_CM_PER_GPIO5_CLKCTRL  = (0x01<<1);    *CTRL_CONF_UART3_RXD  &= ~(0x7<<0 | 0x01<<16 | 0x01<<17 | 0x01<<18);    *CTRL_CONF_UART3_RXD  |=  (0x7<<0 | 0x01<<17);    *GPIO_OE              &= ~(0x01<<minor);    *GPIO_SETDATAOUT      |=  (0x01<<minor);    return 0;     }   static ssize_t leds_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)  {      int  minor = iminor(file->f_inode);    char buf;      printk(KERN_INFO"%s OK.\n",__func__);    if(count != 1){        printk(KERN_INFO"write count != 1.\n");         return 1;    }    if (copy_from_user(&buf, user_buf, count))        return -EFAULT;    if (0x01 == buf)          *GPIO_DATAOUT |=  (0x01<<minor);        else if(0x00 == buf)        *GPIO_DATAOUT &= ~(0x01<<minor);    return 0;  }  static struct file_operations leds_fops = {    .owner  =   THIS_MODULE,      .open   =   leds_drv_open,         .write  =   leds_drv_write,    };static int leds_drv_init(void){    //1.申请设备号    dev_t devid;    printk(KERN_INFO"%s OK.\n",__func__);    if(alloc_chrdev_region(&devid, 0, TI_LEDS_CNT, "ti_leds") < 0)    {        printk(KERN_INFO"%s ERROR.\n",__func__);        goto error;    }    major = MAJOR(devid);    //2.注册到系统中    cdev_init(&leds_cdev, &leds_fops);            cdev_add(&leds_cdev, devid, TI_LEDS_CNT);       leds_cls = class_create(THIS_MODULE, "ti_leds");    device_create(leds_cls, NULL, MKDEV(major, 0), NULL, "ti_led0");     device_create(leds_cls, NULL, MKDEV(major, 1), NULL, "ti_led1");     device_create(leds_cls, NULL, MKDEV(major, 2), NULL, "ti_led2");     device_create(leds_cls, NULL, MKDEV(major, 3), NULL, "ti_led3");    //3.硬件相关    PRCM_CM_PER_GPIO5_CLKCTRL = ioremap(0x44DF8800+0x498, 0x04*1);    CTRL_CONF_UART3_RXD       = ioremap(0x44E10000+0xA28, 0x04*4);    CTRL_CONF_UART3_TXD       = CTRL_CONF_UART3_RXD + 1;    CTRL_CONF_UART3_CTSN      = CTRL_CONF_UART3_RXD + 2;    CTRL_CONF_UART3_RTSN      = CTRL_CONF_UART3_RXD + 3;     GPIO_OE                   = ioremap(0x48322000+0x134, 0x04);     GPIO_DATAOUT              = ioremap(0x48322000+0x13C, 0x04);    GPIO_SETDATAOUT           = ioremap(0x48322000+0x194, 0x04);error:    unregister_chrdev_region(MKDEV(major, 0), TI_LEDS_CNT);    return 0;}static void leds_drv_exit(void){    unsigned i;    printk(KERN_INFO"%s OK.\n",__func__);    for(i=0;i<TI_LEDS_CNT;i++)    {        device_destroy(leds_cls,  MKDEV(major, i));     }    class_destroy(leds_cls);    cdev_del(&leds_cdev);    unregister_chrdev(major, "ti_leds");     iounmap(PRCM_CM_PER_GPIO5_CLKCTRL);    iounmap(CTRL_CONF_UART3_RXD);    iounmap(GPIO_OE);    iounmap(GPIO_DATAOUT);    iounmap(GPIO_SETDATAOUT);}module_init(leds_drv_init);module_exit(leds_drv_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hceng <huangcheng.job@foxmail.com>");MODULE_DESCRIPTION("TI am437x board leds drvice");MODULE_ALIAS("character device:ti_leds");MODULE_VERSION("V1.0");

2.2测试代码(跑马灯)

{[leds_app.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v1.0/leds_app.c }

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <unistd.h>#define msleep(x) usleep(x*1000)int main(int argc, char **argv){    int fd[4];    int val = 0;    int i = 0;    //const char *dev[] = {"/dev/ti_led0", "/dev/ti_led1", "/dev/ti_led2", "/dev/ti_led3"};    const char *dev[] = {"/dev/ti_led2", "/dev/ti_led0", "/dev/ti_led3", "/dev/ti_led1"};    for(i=0; i<4; i++)    {        fd[i] = open(dev[i], O_RDWR);        if (fd[i] < 0)        {            printf("can't open %s\n", *dev[i]);            return 0;        }    }    //leds off all.    for(i=0; i<4; i++)    {        write(fd[i], &val, 1);    }       //flicker leds.    while(1)    {        val = !val;        for(i=0; i<4; i++)        {            write(fd[i], &val, 1);            msleep(300);        }       }}

2.3关于printk调试

内核的printk定义了如下的打印等级:

#define KERN_EMERG        "<0>" /* system is unusable */#define KERN_ALERT        "<1>" /* action must be taken immediately */#define KERN_CRIT         "<2>" /* critical conditions */#define KERN_ERR          "<3>" /* error conditions */#define KERN_WARNING      "<4>" /* warning conditions */#define KERN_NOTICE       "<5>" /* normal but significant condition */#define KERN_INFO         "<6>" /* informational */#define KERN_DEBUG        "<7>" /* debug-level messages */
  • 如果使用串口登陆,可通过修改/proc/sys/kernel/printk里的参数进行设置:
echo "8  4    1    7" >/proc/sys/kernel/printk

上面的四个数字分别代表:

控制台日志级别:优先级[s1] 高于该值的消息将被打印至控制台,[s1]数值越小,优先级越高;
默认的消息日志级别:将用该优先级来打印没有优先级的消息;
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级);
默认的控制台日志级别:控制台日志级别的缺省值;

  • 如果使用SSH登陆,是无法显示printk的打印信息的,但打印的数据会被放在/var/log/messages/proc/kmsg中,利用这一特性,可以后台运行tail命令进行侦测:
tail -f /var/log/messages &

缺点是不能设置打印等级,同时内核的其它信息也会被打印出来。

3.设备驱动模型驱动

关于设备驱动模型前面以及有点了解,在写驱动的时候,主要体现在将原本的硬件资源信息抽取了出来,单独放在了一个文件里,当两个文件的分别加载的时候,根据名字匹配,匹配成功则调用probe()函数,进行类似前面的init()进行初始化。其它的内容基本一样,该干嘛就干嘛。

3.1驱动代码

{ [leds_dev.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v2.0/leds_dev.c }

#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/device.h>#include <linux/platform_device.h>/**************************************************  TI_BOARD  ---------------------------------------------------  Ball     Color       Mode             Pin  ---------------------------------------------------  H24     D7_Blue      0x07     uart3_txd(GPIO5_3)  H25     D8_Blue      0x07     uart3_rxd(GPIO5_2)  K24     D9_Green     0x07     uart3_rtsn(GPIO5_1)  H22     D10_Red      0x07     uart3_ctsn(GPIO5_0) **************************************************/static struct resource leds_resource[] = {  //PRCM_CM_PER_GPIO5_CLKCTRL(498h)    [0] = {          .start = 0x44DF8800,          .end   = 0x44DFFFFF,         .name  = "CM_PER",        .flags = IORESOURCE_MEM,    },  //CTRL_CONF_UART3_RXD(A28h)、CTRL_CONF_UART3_TXD(A2Ch)、CTRL_CONF_UART3_CTSN(A30h)、CTRL_CONF_UART3_RTSN((A34h))    [1] = {          .start = 0x44E10000,          .end   = 0x44E1FFFF,         .name  = "CONTROL_MODULE",        .flags = IORESOURCE_MEM,    },  //GPIO_OE(134h)、GPIO_SETDATAOUT(194h)、GPIO_DATAOUT(13Ch)    [2] = {         .start = 0x48322000,          .end   = 0x48322FFF,         .name  = "GOIP5",        .flags = IORESOURCE_MEM,    },    [3] = {         .start = 0,          .end   = 3,         .name  = "GOIP5_PIN",        .flags = IORESOURCE_IO,    }};  static void leds_release(struct device * dev)  {      printk(KERN_INFO"%s OK.\n",__func__);}static struct platform_device leds_dev = {    .name          = "ti_am437x_leds_platform",    .id            = -1,      .num_resources = ARRAY_SIZE(leds_resource),      .resource      = leds_resource,      .dev = {           .release = leds_release,       },     };static int leds_dev_init(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    return platform_device_register(&leds_dev);;  }  static void leds_dev_exit(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    platform_device_unregister(&leds_dev);  }  module_init(leds_dev_init);module_exit(leds_dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hceng <huangcheng.job@foxmail.com>");MODULE_DESCRIPTION("TI am437x board leds drvice");MODULE_ALIAS("platform:ti_leds");MODULE_VERSION("V2.0");

{[leds_drv.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v2.0/leds_drv.c }

#include <linux/module.h>  #include <linux/version.h>  #include <linux/init.h>  #include <linux/fs.h>  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/sched.h>  #include <linux/pm.h>  #include <linux/sysctl.h>  #include <linux/proc_fs.h>  #include <linux/delay.h>  #include <linux/platform_device.h>  #include <linux/input.h>  #include <linux/irq.h>  #include <asm/uaccess.h>  #include <asm/io.h> #include <linux/cdev.h>#include <asm/uaccess.h>#define TI_LEDS_CNT     4int major;static struct cdev leds_cdev;static struct class *leds_cls;static volatile unsigned long *PRCM_CM_PER_GPIO5_CLKCTRL = NULL;  static volatile unsigned long *CTRL_CONF_UART3_RXD = NULL;  static volatile unsigned long *CTRL_CONF_UART3_TXD = NULL;  static volatile unsigned long *CTRL_CONF_UART3_CTSN = NULL;static volatile unsigned long *CTRL_CONF_UART3_RTSN = NULL;static volatile unsigned long *GPIO_OE = NULL;static volatile unsigned long *GPIO_SETDATAOUT = NULL;static volatile unsigned long *GPIO_DATAOUT = NULL;   static int leds_drv_open(struct inode *inode, struct file *file)  {        int minor = iminor(file->f_inode);    printk(KERN_INFO"%s OK.\n",__func__);    *PRCM_CM_PER_GPIO5_CLKCTRL  = (0x01<<1);    *CTRL_CONF_UART3_RXD  &= ~(0x7<<0 | 0x01<<16 | 0x01<<17 | 0x01<<18);    *CTRL_CONF_UART3_RXD  |=  (0x7<<0 | 0x01<<17);    *GPIO_OE              &= ~(0x01<<minor);    *GPIO_SETDATAOUT      |=  (0x01<<minor);    return 0;     }   static ssize_t leds_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)  {      int minor = iminor(file->f_inode);    char buf;     printk(KERN_INFO"%s OK.\n",__func__);    if(count != 1){        printk(KERN_INFO"write count != 1.\n");         return 1;    }    if (copy_from_user(&buf, user_buf, count))        return -EFAULT;    if (0x01 == buf)          *GPIO_DATAOUT |=  (0x01<<minor);        else if(0x00 == buf)        *GPIO_DATAOUT &= ~(0x01<<minor);    return 0;  }  static struct file_operations leds_fops = {      .owner  =   THIS_MODULE,       .open   =   leds_drv_open,           .write  =   leds_drv_write,       }; static int leds_probe(struct platform_device *pdev)  {      struct resource *res;      dev_t devid;    printk(KERN_INFO"%s OK.\n",__func__);    //1.申请设备号    if(alloc_chrdev_region(&devid, 0, TI_LEDS_CNT, "ti_leds") < 0)    {        printk("%s ERROR\n",__func__);        goto error;    }    major = MAJOR(devid);    //2.注册到系统中    cdev_init(&leds_cdev, &leds_fops);            cdev_add(&leds_cdev, devid, TI_LEDS_CNT);       leds_cls = class_create(THIS_MODULE, "ti_leds");    device_create(leds_cls, NULL, MKDEV(major, 0), NULL, "ti_led0");     device_create(leds_cls, NULL, MKDEV(major, 1), NULL, "ti_led1");     device_create(leds_cls, NULL, MKDEV(major, 2), NULL, "ti_led2");     device_create(leds_cls, NULL, MKDEV(major, 3), NULL, "ti_led3");    //3.硬件相关    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "CM_PER");    if (!res)         return -EINVAL;     PRCM_CM_PER_GPIO5_CLKCTRL = ioremap(res->start+0x498, 0x04*1);    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "CONTROL_MODULE");     if (!res)         return -EINVAL;     CTRL_CONF_UART3_RXD         = ioremap(res->start+0xA28, 0x04*4);    CTRL_CONF_UART3_TXD         = CTRL_CONF_UART3_RXD + 1;    CTRL_CONF_UART3_CTSN        = CTRL_CONF_UART3_RXD + 2;    CTRL_CONF_UART3_RTSN        = CTRL_CONF_UART3_RXD + 3;     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "GOIP5");     if (!res)         return -EINVAL;     GPIO_OE                     = ioremap(res->start+0x134, 0x04);     GPIO_DATAOUT                = ioremap(res->start+0x13C, 0x04);    GPIO_SETDATAOUT             = ioremap(res->start+0x194, 0x04);    *PRCM_CM_PER_GPIO5_CLKCTRL  = (0x01<<1);//使能GPIO外设时钟error:    unregister_chrdev_region(MKDEV(major, 0), TI_LEDS_CNT);     return 0;  }  static int leds_remove(struct platform_device *pdev)  {      unsigned i;    printk(KERN_INFO"%s OK.\n",__func__);    for(i=0;i<TI_LEDS_CNT;i++)    {        device_destroy(leds_cls,  MKDEV(major, i));     }    class_destroy(leds_cls);    cdev_del(&leds_cdev);    unregister_chrdev(major, "ti_leds");     iounmap(PRCM_CM_PER_GPIO5_CLKCTRL);    iounmap(CTRL_CONF_UART3_RXD);    iounmap(GPIO_OE);    iounmap(GPIO_DATAOUT);    iounmap(GPIO_SETDATAOUT);    return 0;  }struct platform_driver leds_drv = {      .probe      = leds_probe,      .remove     = leds_remove,      .driver     = {          .name   = "ti_am437x_leds_platform",      }  };  static int leds_drv_init(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    return platform_driver_register(&leds_drv);  }  static void leds_drv_exit(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    platform_driver_unregister(&leds_drv);  }  module_init(leds_drv_init);module_exit(leds_drv_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hceng <huangcheng.job@foxmail.com>");MODULE_DESCRIPTION("TI am437x board leds drvice");MODULE_ALIAS("platform:ti_leds");MODULE_VERSION("V2.0");

测试程序同前面的一样。

4.设备树驱动

4.1初识设备树

1)前面的总线设备驱动模型中,硬件资源来自于leds_dev.c里面的信息,这样会导致不同的板子,会添加不同的硬件资源信息,造成内核的臃肿。
2)使用设备树后,内核不再包含硬件的描述,硬件描述放在单独的DTS里面,然后编译成二进制的DTB,在U-Boot启动的时候加载进去,然后内核进行解析。
3)DTS、DTC和DTB之间的关系:
DTS经过DTC编译得到DTB,DTB通过DTC反编译得到DTS.

4)ARM中,所有的DTS文件放在arch/arm/boot/dts目录中,为了简化,将Soc公用部分提取了出来作为dtsi,类似头文件。
5)DTC编译工具的源代码在scripts/dtc目录中,编译内核时,编译内核时,需要使能才能将源码编译成工具,对应于scripts/dtc/Makefile"hostprogs-y:=dtc"。Ubuntu也可直接安装DTC工具:

sudo apt-get install device-tree-compiler

6)内核的arch/arm/boot/dts/Makefile中,描述了当某种Soc被选中后,哪些.dtb会编译出来。执行make dtbs,会根据arch/arm/Makefile编译指定目标。
7)单独编译与反编译:

./scripts/dtc/dtc -I dts -O dtb -o xxx.dtb arch/arm/boot/dts/xxx.dts   //dts->dtb./scripts/dtc/dtc -I dtb -O dts -o xxx.dts arch/arm/boot/dts/xxx.dtb   //dtb->dts

8)后面认识深刻了,再总结总结。

4.2修改AM437x设备树

AM437x的设备树文件在~/ti-processor-sdk-linux-am437x-evm-01.00.00.03/board-support/linux-3.14.43+gitAUTOINC+875c69b2c3-g875c69b/arch/arm/boot/dts/中,主要是am4372.dtsiam437x-sk-evm.dts

  • 我的目的是希望写个设备树框架的LED程序,因此想让am437x-sk-evm.dts干净点,只包含LED硬件描述,因此我需要删除am437x-sk-evm.dts里面的其它硬件描述。经过测试,am437x-sk-evm.dts里面包含部分MMC的描述,一旦删除将不能成功启动内核。而且,后面调试的时候,希望开发板通过NFS挂载的方式,直接加载编译的驱动模块,因此需要保留网卡描述部分。最后,将MMC和网卡必须的部分,提取了出来,放在了am4372.dtsi中。精简后的am437x-sk-evm.dts内容如下:

{[am437x-sk-evm.dts] https://github.com/hceng/am437x/blob/master/drive/1th_led/v3.0/am437x-sk-evm.dts }

/* AM437x SK EVM *//dts-v1/;#include "am4372.dtsi"#include <dt-bindings/pinctrl/am43xx.h>#include <dt-bindings/gpio/gpio.h>/ {    model = "TI AM437x SK EVM";    compatible = "ti,am437x-sk-evm","ti,am4372","ti,am43";    led_pin {        compatible    = "ti_leds";        pinctrl-names = "default";        pinctrl-0 = <&leds_pins>;        am437x,led_gpio0 = <&gpio5 0 GPIO_ACTIVE_HIGH>;        am437x,led_gpio1 = <&gpio5 1 GPIO_ACTIVE_HIGH>;        am437x,led_gpio2 = <&gpio5 2 GPIO_ACTIVE_HIGH>;        am437x,led_gpio3 = <&gpio5 3 GPIO_ACTIVE_HIGH>;    };};&am43xx_pinmux {    leds_pins: leds_pins {        pinctrl-single,pins = <            0x228 (PIN_OUTPUT | MUX_MODE7)  /* uart3_rxd.gpio5_2 */            0x22c (PIN_OUTPUT | MUX_MODE7)  /* uart3_txd.gpio5_3 */            0x230 (PIN_OUTPUT | MUX_MODE7)  /* uart3_ctsn.gpio5_0 */            0x234 (PIN_OUTPUT | MUX_MODE7)  /* uart3_rtsn.gpio5_1 */        >;    };};&gpio5 {    status = "okay";};

额,在调试的过程中,需要不断编译新的DTB和复制到SD卡的rootfs分区中,仿照前面写了个脚本进行自动编译和复制,同时检查文件的生成时间间隔,实际中,确实减少了焦躁的重复操作。

4.3驱动代码

{[leds_drv.c] https://github.com/hceng/am437x/blob/master/drive/1th_led/v3.0/leds_drv.c}

#include <linux/module.h>  #include <linux/version.h>  #include <linux/init.h>  #include <linux/fs.h>  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/sched.h>  #include <linux/pm.h>  #include <linux/sysctl.h>  #include <linux/proc_fs.h>  #include <linux/delay.h>  #include <linux/platform_device.h>  #include <linux/input.h>  #include <linux/irq.h>  #include <asm/uaccess.h>  #include <asm/io.h> #include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/gpio.h>#include <linux/of_gpio.h>#define TI_LEDS_CNT     4int major;static struct cdev leds_cdev;static struct class *leds_cls;static int led0,led1,led2,led3; static int leds_drv_open(struct inode *inode, struct file *file)  {        printk(KERN_INFO"%s OK.\n",__func__);     return 0;     }   static ssize_t leds_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t * ppos)  {      int minor = iminor(file->f_inode);     char buf;    printk(KERN_INFO"%s OK.\n",__func__);    if(count != 1){        printk(KERN_INFO"write count != 1.\n");         return 1;    }    if (copy_from_user(&buf, user_buf, count))        return -EFAULT;    if(0x01 == buf)    {        switch(minor){        case 0:            gpio_set_value(led0, 0);            break;        case 1:            gpio_set_value(led1, 0);            break;        case 2:            gpio_set_value(led2, 0);            break;        case 3:            gpio_set_value(led3, 0);            break;        default:            printk(KERN_INFO"%s receive minor error.\n",__func__);        }                           }    else if(0x00 == buf)    {        switch(minor){        case 0:            gpio_set_value(led0, 1);            break;        case 1:            gpio_set_value(led1, 1);            break;        case 2:            gpio_set_value(led2, 1);            break;        case 3:            gpio_set_value(led3, 1);            break;        default:            printk(KERN_INFO"%s receive minor error\n",__func__);        }           }    return 0;  }  static struct file_operations leds_fops = {      .owner  =   THIS_MODULE,       .open   =   leds_drv_open,           .write  =   leds_drv_write,       }; static int leds_probe(struct platform_device *pdev)  {      struct device *dev = &pdev->dev;    dev_t devid;    printk(KERN_INFO"%s OK.\n",__func__);    //1.申请设备号    if(alloc_chrdev_region(&devid, 0, TI_LEDS_CNT, "ti_leds") < 0)    {        printk(KERN_INFO"%s ERROR.\n",__func__);        goto error;    }    major = MAJOR(devid);    //2.注册到系统中    cdev_init(&leds_cdev, &leds_fops);            cdev_add(&leds_cdev, devid, TI_LEDS_CNT);       leds_cls = class_create(THIS_MODULE, "ti_leds");    device_create(leds_cls, NULL, MKDEV(major, 0), NULL, "ti_led0");     device_create(leds_cls, NULL, MKDEV(major, 1), NULL, "ti_led1");     device_create(leds_cls, NULL, MKDEV(major, 2), NULL, "ti_led2");     device_create(leds_cls, NULL, MKDEV(major, 3), NULL, "ti_led3");    //3.硬件相关    led0 = of_get_named_gpio(dev->of_node, "am437x,led_gpio0", 0);;    led1 = of_get_named_gpio(dev->of_node, "am437x,led_gpio1", 0);;    led2 = of_get_named_gpio(dev->of_node, "am437x,led_gpio2", 0);;    led3 = of_get_named_gpio(dev->of_node, "am437x,led_gpio3", 0);    //printk(KERN_INFO"led0 = %d\n",led0);    //printk(KERN_INFO"led1 = %d\n",led1);    //printk(KERN_INFO"led2 = %d\n",led2);    //printk(KERN_INFO"led3 = %d\n",led3);    devm_gpio_request_one(dev, led0, GPIOF_OUT_INIT_HIGH, "LED0");    devm_gpio_request_one(dev, led1, GPIOF_OUT_INIT_HIGH, "LED1");    devm_gpio_request_one(dev, led2, GPIOF_OUT_INIT_HIGH, "LED2");    devm_gpio_request_one(dev, led3, GPIOF_OUT_INIT_HIGH, "LED3");error:    unregister_chrdev_region(MKDEV(major, 0), TI_LEDS_CNT);     return 0;  }  static int leds_remove(struct platform_device *pdev)  {      unsigned i;    printk(KERN_INFO"%s OK.\n",__func__);    for(i=0;i<TI_LEDS_CNT;i++)    {        device_destroy(leds_cls,  MKDEV(major, i));     }    class_destroy(leds_cls);    cdev_del(&leds_cdev);    unregister_chrdev(major, "ti_leds");     return 0;  }static const struct of_device_id of_gpio_leds_match[] = {    { .compatible = "ti_leds", },    {},};static struct platform_driver leds_drv = {    .probe      = leds_probe,    .remove     = leds_remove,    .driver     = {        .name   = "ti_am437x_leds_platform",        .owner  = THIS_MODULE,        .of_match_table = of_match_ptr(of_gpio_leds_match),    },};static int leds_drv_init(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    return platform_driver_register(&leds_drv);  }  static void leds_drv_exit(void)  {      printk(KERN_INFO"%s OK.\n",__func__);    platform_driver_unregister(&leds_drv);  }  module_init(leds_drv_init);module_exit(leds_drv_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hceng <huangcheng.job@foxmail.com>");MODULE_DESCRIPTION("TI am437x board leds drvice");MODULE_ALIAS("platform:device tree:ti_leds");MODULE_VERSION("V3.0");

测试程序同前面的一样。

6.心得

在我理解到驱动=裸机+软件框架的时候,我对之前的裸机也就没那么排斥了。
而且这个软件框架,就现在来看,核心的那几步像:申请设备号、注册设备、创建类和创建节点这些都不变

原创粉丝点击