全志平台boot框架中增加设备驱动过程分析


 

       在boot启动阶段,大家都知道他的主要目的就是引导uboot,uboot在引导内核,从而让整个系统运作起来。全志的boot阶段,对应平板这一块,它会驱动LCD,显示一些开机LOGO,这个过程很快,也就1-2秒钟的时间。然而对于车载行业应用来说,可能需要再boot阶段做一些事情。比如,机器冷启动,大家都知道android启动时间还是比较长的,那么怎么使得客户能快速的用上倒车影像的功能呢?这就需要动脑筋了。

/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/edsam49原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

         我们可以肯定的是不能等到系统启动完成,应该在最早我们能控制的地方增加我们的特定代码。倒车影像说白了,就是一个视频信号输入到系统,在LCD上再显示出来的事。但是如果要在boot阶段就把视频输入信号正常的显示在LCD上的话,我们需要做什么呢?

        首先,我们需要做倒车信号的检测。倒车信号的检测一般来说都是一个GPIO的高低电平来评判,而直接连接倒车线的IC一般是单片机,单片机处理识别以后再告诉主控芯片,告诉的通道其实也就是控制一个GPIO,主控这边来监控这个GPIO的变化,通常是设置成中断模式来检测。全志平台最方便的就是驱动的高度可配置化,非常灵活。我们可以把倒车检测的GPIO配置放在配置文件里面,也就是sys_config.fex文件里面,放在这里面的话就很灵活,不同项目如果使用的IO脚步一样,轻松一配,不用改boot的代码,相当不错。简单示例代码如下:

  1. ret = wBoot_script_parser_fetch("custom_design_cfg""ReverseCarDetectGPIO", (int *)&gpio_reverse, sizeof(user_gpio_set_t)/4);  
  2. if(!ret)  
  3. {  
  4.  gpio_reverse.mul_sel = 0; //set input type   
  5.  gpio_handle = wBoot_GPIO_Request(&gpio_reverse, 1);  
  6.  if(gpio_handle)  
  7.  {  
  8.      gpio_value = wBoot_GPIO_Read_One_PIN_Value(gpio_handle, 0);  
  9.  }  
  10. }  
  11.   
  12.   
  13. if(1 == gpio_value)  
  14.    {  
  15.     //__inf("Reverse signal come\n");   
  16. return 1;  
  17.    }  
  18.    else   
  19.    {  
  20.     __inf("Not enter reverse!!!\n");  
  21. return 0;  
  22.    }  
	 ret = wBoot_script_parser_fetch("custom_design_cfg", "ReverseCarDetectGPIO", (int *)&gpio_reverse, sizeof(user_gpio_set_t)/4);
	 if(!ret)
	 {
		 gpio_reverse.mul_sel = 0; //set input type
		 gpio_handle = wBoot_GPIO_Request(&gpio_reverse, 1);
		 if(gpio_handle)
		 {
			 gpio_value = wBoot_GPIO_Read_One_PIN_Value(gpio_handle, 0);
		 }
	 }


	 if(1 == gpio_value)
     {
	 	//__inf("Reverse signal come\n");
		return 1;
     }
     else 
     {
	 	__inf("Not enter reverse!!!\n");
		return 0;
     }

        接下来我们就要做倒车显示处理了。做过全志平台的人都知道,里面有一个TVD的module来负责视频输入信号的处理。那么,我们就需要把TVD给打通。为了日后的管理方便,在boot里面这些驱动也都已经模块化,因此我们也要取其精华,把好的做法延续下去。就是单独做一个模块化的驱动。那怎么说呢?我教你,告诉你我也是从它已有的代码堆里模仿处理的,但是里面还是有几个地方要注意了,不要全抄了啊,免得老师发现你考试的卷子把别人的名字都抄过来了就不好了。

言归正传。搭框架,学样子。在boot1目录下的driver目录里,先增加一个tvd驱动的目录。把drv_de目录下的make.cfg、makefile、config.lds、magic.c先直接copy过来。先把make.cfg修改一下,主要是改名字,基本如下:

  1. #定义生成的目标文件(输出/本地)  
  2. LTARGET     = drv_tvd.drv  
  3. TARGET0     = $(WORKSPACEPATH)/wboot/bootfs/drv_tvd.drv  
  4. TARGET1     = $(LICHEEPATH)/wboot/bootfs/drv_tvd.drv  
  5. TARGET2     = $(SDKROOT)/pack/chips/sun7i/wboot/bootfs/drv_tvd.drv  
  6. LOCALTARGET = __image.axf  
#定义生成的目标文件(输出/本地)
LTARGET     = drv_tvd.drv
TARGET0     = $(WORKSPACEPATH)/wboot/bootfs/drv_tvd.drv
TARGET1     = $(LICHEEPATH)/wboot/bootfs/drv_tvd.drv
TARGET2     = $(SDKROOT)/pack/chips/sun7i/wboot/bootfs/drv_tvd.drv
LOCALTARGET = __image.axf

         Makefile写的比较好适配,不用修改。Config.lds这可得说到说到,哥在里面吃了亏的,当时也是直接拷过来的,后面再多个驱动一起运作的时候,工作不正常,查了半天才想起这个破绽,就是程序的加载地址。说出来了,感觉很容易是不是,没搞懂之前那还真不是太好找。就是0x42960000这一串数字,你得看其他模块的这个对应位置的值是多少,根据driver的大小,留一段给上一个程序。Tvd的如下:

  1. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")  
  2. OUTPUT_ARCH(arm)  
  3. SECTIONS  
  4. {  
  5.     . = 0x42960000;  
  6.      EGON2_MAGIC : { magic.o(.rodata) }  
  7.     .text : { *(.text) *(.rodata)}  
  8.     .data   : { *(.data)   }  
  9.     .bss    : { *(.bss)    }  
  10.   
  11. }  
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SECTIONS
{
	. = 0x42960000;
	 EGON2_MAGIC : { magic.o(.rodata) }
	.text : { *(.text) *(.rodata)}
	.data   : { *(.data)   }
	.bss    : { *(.bss)    }

}

      剩下的我们就得改magic.c这个文件了,在这个文件里面也就是依葫芦画瓢了。需要改的就是模块的ID编号,其他就是对应改改函数接口的名字,如下:

  1. const eGon2_mod_t modinfo =  
  2. {  
  3.     {'e','G','o','n','2','d','r','v'},      //.magic  
  4.     0x01000000,                             //.version  
  5.     EGON2_EMOD_TYPE_DRV_TVD,                //.mod id  
  6.     0,                                      //.入口地址,驱动(模块)应该填空  
  7.     {                                       //.mif  
  8.         &DRV_TVD_INIT,  
  9.         &DRV_TVD_EXIT,  
  10.         &DRV_TVD_OPEN,  
  11.         &DRV_TVD_CLOSE,  
  12.         &DRV_TVD_READ,  
  13.             &DRV_TVD_WRITE,  
  14.         &DRV_TVD_IOCTRL,  
  15.         &DRV_TVD_Standby  
  16.     }  
  17. };  
const eGon2_mod_t modinfo =
{
    {'e','G','o','n','2','d','r','v'},		//.magic
    0x01000000,                				//.version
    EGON2_EMOD_TYPE_DRV_TVD,                //.mod id
    0,                                      //.入口地址,驱动(模块)应该填空
	{                                       //.mif
	    &DRV_TVD_INIT,
	    &DRV_TVD_EXIT,
	    &DRV_TVD_OPEN,
	    &DRV_TVD_CLOSE,
	    &DRV_TVD_READ,
    	    &DRV_TVD_WRITE,
	    &DRV_TVD_IOCTRL,
	    &DRV_TVD_Standby
	}
};

     接下来,我们就该写驱动的实体了吧!也即是magic.c里面的各个函数。在这里可以先都只写一行打印信息,其他为空的函数。

格式就按下面的要求来就好了,如下:

  1. struct eGon2_drv_func  
  2. {  
  3.     int          (* eGon2_init  )(void                                                                         );  
  4.     int          (* eGon2_exit  )(void                                                                         );  
  5.     unsigned int (* eGon2_open  )(unsigned int  mid, void   * open_arg                                         );  
  6.     int          (* eGon2_close )(unsigned int  hd                                                             );  
  7.     unsigned int (* eGon2_read  )(void       *pdata, unsigned int size, unsigned int n  , unsigned int  hd     );  
  8.     unsigned int (* eGon2_write )(const void *pdata, unsigned int size, unsigned int n  , unsigned int  hd     );  
  9.     int          (* eGon2_ioctl )(unsigned int  hd , unsigned int cmd , signed int aux,   void         *pbuffer);  
  10.     int          (* eGon2_standby)(unsigned int cmd ,void    *pbuffer                                          );  
  11. };  
  12.   
  13. //eGon2里用到的模块数据结构,里面区分了是一个驱动还是一个应用  
  14. typedef struct _eGon2_mod_section  
  15. {  
  16.     char                     magic[8];                          //MAGIC字符,用于标识是eGON2的驱动/应用代码  
  17.     unsigned int             version;                           //版本数字  
  18.     unsigned int             mod_id;                            //模块的ID,每个elf都应该有一个模块ID,不论驱动还是应用,且各不相同  
  19.     int                     (*main)(int argc, char **argv);     //pcb里第一个任务的首地址  
  20.     struct  eGon2_drv_func   demo_func;                         //驱动函数列表,列出了驱动所必要的7个函数  
  21. }  
  22. eGon2_mod_t;  
struct eGon2_drv_func
{
    int          (* eGon2_init  )(void                                                                         );
    int          (* eGon2_exit  )(void                                                                         );
    unsigned int (* eGon2_open  )(unsigned int  mid, void   * open_arg                                         );
    int          (* eGon2_close )(unsigned int  hd                                                             );
    unsigned int (* eGon2_read  )(void       *pdata, unsigned int size, unsigned int n  , unsigned int  hd     );
    unsigned int (* eGon2_write )(const void *pdata, unsigned int size, unsigned int n  , unsigned int  hd     );
    int          (* eGon2_ioctl )(unsigned int  hd , unsigned int cmd , signed int aux,   void         *pbuffer);
    int			 (* eGon2_standby)(unsigned int cmd ,void    *pbuffer										   );
};

//eGon2里用到的模块数据结构,里面区分了是一个驱动还是一个应用
typedef struct _eGon2_mod_section
{
    char                     magic[8];                          //MAGIC字符,用于标识是eGON2的驱动/应用代码
    unsigned int             version;                           //版本数字
    unsigned int             mod_id;                            //模块的ID,每个elf都应该有一个模块ID,不论驱动还是应用,且各不相同
    int                     (*main)(int argc, char **argv);     //pcb里第一个任务的首地址
    struct  eGon2_drv_func   demo_func;                         //驱动函数列表,列出了驱动所必要的7个函数
}
eGon2_mod_t;

      然后编译一下整个模块,可以加到boot的整体编译里面,也就是在boot目录下的Makefile 里面增加一行     “make -Cboot1/driver/drv_tvd -j8”即可。这样make一下,就会去编译这个新增加的模块,编译没有问题就会有一个二进制文件出来。

        这样整个驱动模块的框架是已经搭建好了。可以在bootmain里面去调用这个驱动了。加载驱动也是用的boot框架代码里支持的wBoot_driver_install("c:\\drv_tvd.drv");, 当然还有其他ioctl,close这些接口,就不详细在此描述。本文只是重点讲述模块的生成。关于模块的使用,已经TVD怎么工作的,显示控制怎么管理,笔者计划在下一篇文章中再做叙述,敬请关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值