Linux设备模型(总线、设备、驱动程序和类)之三:device_driver && 多厂家驱动自动识别

      设备模型跟踪所有系统已知的驱动,主要目的是使驱动程序核心能协调驱动和新设备之间的关系。一旦驱动在系统中是已知的对象就可能完成大量的工作。驱动程序的结构体device_driver 定义如下:
struct device_driver {
     const char  *name;       /*驱动程序的名字( 在 sysfs 中出现 )*/
     struct bus_type  *bus;   /*驱动程序所操作的总线类型*/

     struct module  *owner;
     const char   *mod_name; /* used for built-in modules */

     int (*probe) (struct device *dev);
     int (*remove) (struct device *dev);
     void (*shutdown) (struct device *dev);
     int (*suspend) (struct device *dev, pm_message_t state);
     int (*resume) (struct device *dev);


     struct attribute_group **groups;

     struct pm_ops *pm;
     struct driver_private *p;
};
(1)驱动程序的注册和注销
/*注册device_driver 结构的函数是:*/
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
(2)驱动程序的属性
/*driver的属性结构在:*/
struct driver_attribute {
     struct attribute attr;
     ssize_t (*show)(struct device_driver *drv, char *buf);
     ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
宏定义DRIVER_ATTR(_name,_mode,_show,_store),它的原型是:

#define DRIVER_ATTR(_name, _mode, _show, _store) \
struct driver_attribute driver_attr_##_name =  \
__ATTR(_name, _mode, _show, _store)

而#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show,     \
.store = _store,     \
}

     *属性文件创建的方法:*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
(3)驱动程序结构的嵌入
      对大多数驱动程序核心结构,device_driver 结构通常被嵌入到一个更高层的、总线相关的结构中。当然也有直接注册驱动的,不用嵌入到高层结构体。如driver_register(&wm97xx_driver)。
       以lddbus 子系统为例,它定义了ldd_driver 结构:

struct ldd_driver {
     char *version;
     struct module *module;
     struct device_driver driver;
     struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
      lddbus总线中相关的驱动注册和注销函数是:
static ssize_t show_version(struct device_driver *driver, char *buf)
{
    struct ldd_driver *ldriver = to_ldd_driver(driver);
    sprintf(buf, "%s/n", ldriver->version);
    return strlen(buf);
}
int register_ldd_driver(struct ldd_driver *driver)  //device_driver被嵌入到更高层结构体
{
      int ret;
      driver->driver.bus = &ldd_bus_type;
      ret = driver_register(&driver->driver);/*注册底层的 device_driver 结构到核心*/
      if (ret)
          return ret;
      driver->version_attr.attr.name = "version";/* driver_attribute 结构必须手工填充*/
      driver->version_attr.attr.owner = driver->module;
      driver->version_attr.attr.mode = S_IRUGO;
      driver->version_attr.show = show_version;
      driver->version_attr.store = NULL;
      return driver_create_file(&driver->driver, &driver->version_attr);/*建立版本属性,因为这个属性在运行时被创建,所以不能使用 DRIVER_ATTR 宏*/
}
void unregister_ldd_driver(struct ldd_driver *driver)
{
      driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(register_ldd_driver);
EXPORT_SYMBOL(unregister_ldd_driver);

在sculld 中创建的 ldd_driver 结构如下: /* Device model stuff */
static struct ldd_driver sculld_driver = {
    .version = "$Revision: 1.21 $",
    .module = THIS_MODULE,
    .driver = {
        .name = "sculld",
    },
};

=====================================================================================================================

        关于不同芯片方案的自动识别,比如TP芯片、GSENSOR等的自动识别在实际生产和应用中都是很有必要的,大大简化软件维护和发布流程。以下的说明以MTK的智能机平台为基础,对于其他平台该方法并不适用,因为MTK自己有一套设备驱动管理流程,相当于在同一类驱动上都加了一层管理层。

(1)在注册MTK的TP的platform_driver时,名字是#define TPD_DEVICE            "mtk-tpd",所以其他TP备选芯片的i2c_driver中的driver->name也都必须是TPD_DEVICE,方便设备挂接。为什么一开始不加修改的在配置文件中同时编译两个TP型号,会出现编译错误(可能是运行错误)?因为每个TP的驱动都有i2c_add_driver,不可能在同一个TPD_DEVICE名字上挂接两个I2C设备。

(2)新的方法,就是在第一个编译加载的TP驱动中做读ID号的判断,如果没有识别到正确的ID,认定该芯片不符,则执行i2c_del_driver,以保证后来编译执行的其他TP能够继续挂接上TPD_DEVICE的platform_driver。

(3)过程:以melfas和mstar两颗TP芯片为例,如果前者在后者之前编译加载,后者不做改动,前者驱动做一定的修改。

定义个单文件内的全局变量:static int i2c_tetect =0;

melfas的i2c_driver定义如下:

  1. static struct i2c_driver tpd_i2c_driver = 
  2.     .driver = { 
  3.         .name = TPD_DEVICE, 
  4.         .owner = THIS_MODULE, 
  5.     }, 
  6.     .probe = tpd_probe, 
  7.     .remove = __devexit_p(tpd_remove), 
  8.     .id_table = tpd_id, 
  9.     .detect = tpd_detect, 
  10.     .address_data = &addr_data, 
  11. }; 
static struct i2c_driver tpd_i2c_driver =
{
    .driver = {
        .name = TPD_DEVICE,
        .owner = THIS_MODULE,
    },
    .probe = tpd_probe,
    .remove = __devexit_p(tpd_remove),
    .id_table = tpd_id,
    .detect = tpd_detect,
    .address_data = &addr_data,
};


在它的tpd_probe中开始做个判断,没有读到正确的ID,i2c_tetect=-1;同时该函数也返回-1。注意:tpd_load_status = 1;是在每个TP probe驱动最后中都存在的语句,表示该驱动加载成功。

(4)在melfas的驱动中本地的tpd_local_init中,

  1. if(i2c_add_driver(&tpd_i2c_driver) != 0) 
  2.      printk("melfas unable to add i2c driver.\n"); 
  3.      return -1; 
  4. }              //就算tpd_probe返回-1,标识该驱动没有正确识别到芯片,i2c_add_driver并不一定要返回-1。 
  5.  
  6. if(i2c_tetect!=0)   //不等于0,说明在tpd_probe中赋值,标识判断不成功 
  7.      i2c_del_driver(&tpd_i2c_driver);       //执行i2c_del_driver,留出空位 
  8.      printk("melfas not detect .\n"); 
  9.      return -1; 
    if(i2c_add_driver(&tpd_i2c_driver) != 0)
    {
        printk("melfas unable to add i2c driver.\n");
        return -1;
    }				//就算tpd_probe返回-1,标识该驱动没有正确识别到芯片,i2c_add_driver并不一定要返回-1。

   if(i2c_tetect!=0)	//不等于0,说明在tpd_probe中赋值,标识判断不成功
   {
        i2c_del_driver(&tpd_i2c_driver);		//执行i2c_del_driver,留出空位
        printk("melfas not detect .\n");
        return -1;
    }

(5)在MTK的TPD管理器中,

  1. for(i = 1; i < TP_DRV_MAX_COUNT; i++) 
  2.             /* add tpd driver into list */ 
  3.         if(tpd_driver_list[i].tpd_device_name != NULL) 
  4.         { 
  5.                 tpd_driver_list[i].tpd_local_init(); 
  6.                 if(tpd_load_status ==1) { 
  7.                     TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n", tpd_driver_list[i].tpd_device_name); 
  8.                     g_tpd_drv = &tpd_driver_list[i]; 
  9.                     break
  10.                 } 
  11.         }     
for(i = 1; i < TP_DRV_MAX_COUNT; i++)
{
    		/* add tpd driver into list */
		if(tpd_driver_list[i].tpd_device_name != NULL)
		{
				tpd_driver_list[i].tpd_local_init();
				if(tpd_load_status ==1) {
					TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n", tpd_driver_list[i].tpd_device_name);
					g_tpd_drv = &tpd_driver_list[i];
					break;
				}
		}    
 }

      它会依次执行每个TP驱动的tpd_local_init,只有tpd_load_status为1的才是已经完整PROBE的驱动,即刻退出循环;否则继续下一个TP的匹配寻找。当然,此处也可以已
tpd_local_init函数的返回值作为判断标准,PROBE成功的返回0,不成功的会提前返回-1。

       在实际生产中碰到过一个问题:TP的自动识别是靠读取MELFAS的设备ID号来判断的,如果读取不成功则是MSTAR的TP。但是存在一个问题,如果开机MELFAS的TP在第一次开机升级过程中断电,会造成TP不能工作也不能读ID号,必须强制升级才能保证TP的后续工作。这样当读取ID不成功有两种可能:一是MELFAS的TP,得强制升级;二是MSTAR的TP,所以程序中必须做判断处理。处理的原则必须保证:

1,MSTAR TP可用            (验证OK)
2,MELFAS 正常开机可用  (验证OK)
3,MELFAS 第一次开机升级断电可再次升级并可用(验证OK)
4,MELFAS 第一次开机升级只升一次(验证OK)

参考原文:http://blog.csdn.net/funy_liu/archive/2010/02/25/5322040.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值