精华内容
下载资源
问答
  • 错误原因:训练时指定的设备当前环境下的设备不匹配 解决方法:在恢复.meta的时候,添加一个参数clear devices = True即可清除之前配置。 代码样本如下: saver = tf.train.import_meta_graph('./model.ckpt....

    错误内容:nvalidArgumentError (see above for traceback): Cannot assign a device for operation ‘Reshape’: Operation was explicitly assigned to /job:worker/task:0 but available devices are [ /job:localhost/replica:0/task:0/device:CPU:0, /job:localhost/replica:0/task:0/device:GPU:0 ]. Make sure the device specification refers to a valid device.

    错误原因:训练时指定的设备与当前环境下的设备不匹配

    解决方法:在恢复.meta的时候,添加一个参数clear devices = True即可清除之前配置。
    代码样本如下:

    saver = tf.train.import_meta_graph('./model.ckpt.meta',clear_devices=True)
    

    我也是查了很久,自己无意中看源码看到了这个参数,才解决了问题,所以分享下,帮助后来人!

    展开全文
  • 本系列导航:高通平台8953 Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)高通平台8953 Linux DTS(Device Tree Source)设备树详解之二(DTS设备匹配过程)高通平台8953 Linux DTS(Device Tree ...

    本系列导航:

    高通平台8953  Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)

    高通平台8953 Linux DTS(Device Tree Source)设备树详解之二(DTS设备树匹配过程)

    高通平台8953 Linux DTS(Device Tree Source)设备树详解之三(高通MSM8953 android7.1实例分析篇)


    有上一篇文章,我们了解了dts的背景知识和相关基础,这次我们对应实际设备进行一下相关分析。

     

    DTS设备树的匹配过程

    一个dts文件确定一个项目,多个项目可以包含同一个dtsi文件。找到该项目对应的dts文件即找到了该设备树的根节点。

    kernel\arch\arm\boot\dts\qcom\sdm630-mtp.dts

    [objc] view plain copy
    1. /* Copyright (c) 2017, The Linux Foundation. All rights reserved. 
    2.  * 
    3.  * This program is free software; you can redistribute it and/or modify 
    4.  * it under the terms of the GNU General Public License version 2 and 
    5.  * only version 2 as published by the Free Software Foundation. 
    6.  * 
    7.  * This program is distributed in the hope that it will be useful, 
    8.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
    9.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    10.  * GNU General Public License for more details. 
    11.  */  
    12.   
    13.   
    14. /dts-v1/;  
    15.   
    16. #include "sdm630.dtsi"  
    17. #include "sdm630-mtp.dtsi"  
    18. //#include "sdm660-external-codec.dtsi"  
    19. #include "sdm660-internal-codec.dtsi"  
    20. #include "synaptics-dsx-i2c.dtsi"  
    21.   
    22.   
    23. / {  
    24.     model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660L MTP";  
    25.     compatible = "qcom,sdm630-mtp""qcom,sdm630""qcom,mtp";  
    26.     qcom,board-id = <8 0>;  
    27.     qcom,pmic-id = <0x00010010x01010110x0 0x0>,  
    28.             <0x00010010x02010110x0 0x0>;  
    29. };  
    30.   
    31. &tavil_snd {  
    32.     qcom,msm-mbhc-moist-cfg = <0>, <0>, <3>;  
    33. };  

    当然devicetree的根节点也是需要和板子进行匹配的,这个匹配信息存放在sbl(second boot loader)中,对应dts文件中描述的board-id(上面代码中的qcom,board-id属性),通过共享内存传递给bootloader,由bootloader将此board-id匹配dts文件(devicetree的根节点文件),将由dtc编译后的dts文件(dtb文件)加载到内存,然后在kernel中展开dts树,并且挂载dts树上的所有设备。

    (ps:cat /proc/cmdline 查看cmdline)

     

    Dts中相关符号的含义

    /        根节点

    @     如果设备有地址,则由此符号指定

    &     引用节点

    :        冒号前的label是为了方便引用给节点起的别名,此label一般使用为&label

    ,        属性名称中可以包含逗号。如compatible属性的名字 组成方式为"[manufacturer], [model]",加入厂商名是为了避免重名。自定义属性名中通常也要有厂商名,并以逗号分隔。

    # #并不表示注释。如 #address-cells ,#size-cells 用来决定reg属性的格式。

            空属性并不一定表示没有赋值。如 interrupt-controller 一个空属性用来声明这个node接收中断信号

    数据类型

    “”     引号中的为字符串,字符串数组:”strint1”,”string2”,”string3”

    < >    尖括号中的为32位整形数字,整形数组<12 3 4>

    [ ]      方括号中的为32位十六进制数,十六机制数据[0x11 0x12 0x13]  其中0x可省略


    构成节点名的有效字符:

    0-9a-zA-Z,._+-

    构成属性名的有效字符:

    0-9a-zA-Z,._+?#


    DTS中几个难理解的属性的解释

    a. 地址

    设备的地址特性根据一下几个属性来控制:

    [objc] view plain copy
    1. reg  
    2. #address-cells  
    3. #size-cells  

    reg意为region,区域。格式为:

    [objc] view plain copy
    1. reg = <address1length1 [address2 length2] [address3 length3]>;  

    父类的address-cells和size-cells决定了子类的相关属性要包含多少个cell,如果子节点有特殊需求的话,可以自己再定义,这样就可以摆脱父节点的控制。

    address-cells决定了address1/2/3包含几个cell,size-cells决定了length1/2/3包含了几个cell。本地模块例如:

    [objc] view plain copy
    1. spi@10115000{  
    2.         compatible = "arm,pl022";  
    3.         reg = <0x10115000 0x1000 >;  
    4. };  

    位于0x10115000的SPI设备申请地址空间,起始地址为0x10115000,长度为0x1000,即属于这个SPI设备的地址范围是0x10115000~0x10116000。

    实际应用中,有另外一种情况,就是通过外部芯片片选激活模块。例如,挂载在外部总线上,需要通过片选线工作的一些模块:

    [objc] view plain copy
    1. external-bus{  
    2.     #address-cells = <2>  
    3.     #size-cells = <1>;  
    4.    
    5.     ethernet@0,0 {  
    6.         compatible = "smc,smc91c111";  
    7.         reg = <0 0 0x1000>;  
    8.     };  
    9.    
    10.     i2c@1,0 {  
    11.         compatible ="acme,a1234-i2c-bus";  
    12.         #address-cells = <1>;  
    13.         #size-cells = <0>;  
    14.         reg = <1 0 0x1000>;  
    15.         rtc@58 {  
    16.             compatible ="maxim,ds1338";  
    17.             reg = <58>;  
    18.         };  
    19.     };  
    20.    
    21.     flash@2,0 {  
    22.         compatible ="samsung,k8f1315ebm""cfi-flash";  
    23.         reg = <2 0 0x4000000>;  
    24.     };  
    25. };  

    external-bus使用两个cell来描述地址,一个是片选序号,另一个是片选序号上的偏移量。而地址空间长度依然用一个cell来描述。所以以上的子设备们都需要3个cell来描述地址空间属性——片选、偏移量、地址长度。在上个例子中,有一个例外,就是i2c控制器模块下的rtc模块。因为I2C设备只是被分配在一个地址上,不需要其他任何空间,所以只需要一个address的cell就可以描述完整,不需要size-cells。

    当需要描述的设备不是本地设备时,就需要描述一个从设备地址空间到CPU地址空间的映射关系,这里就需要用到ranges属性。还是以上边的external-bus举例:

    [objc] view plain copy
    1. #address-cells= <1>;  
    2. #size-cells= <1>;  
    3. ...  
    4. external-bus{  
    5.     #address-cells = <2>  
    6.     #size-cells = <1>;  
    7.     ranges = <0 0  0x10100000  0x10000     // Chipselect 1,Ethernet  
    8.               1 0  0x10160000  0x10000     // Chipselect 2, i2c controller  
    9.               2 0  0x30000000  0x1000000>; // Chipselect 3, NOR Flash  
    10. };  

    ranges属性为一个地址转换表。表中的每一行都包含了子地址、父地址、在自地址空间内的区域大小。他们的大小(包含的cell)分别由子节点的address-cells的值、父节点的address-cells的值和子节点的size-cells来决定。以第一行为例:

    ·        0 0 两个cell,由子节点external-bus的address-cells=<2>决定;

    ·        0x10100000 一个cell,由父节点的address-cells=<1>决定;

    ·        0x10000 一个cell,由子节点external-bus的size-cells=<1>决定。
    最终第一行说明的意思就是:片选0,偏移0(选中了网卡),被映射到CPU地址空间的0x10100000~0x10110000中,地址长度为0x10000。

    b. 中断

    描述中断连接需要四个属性:
    1. interrupt-controller 一个空属性用来声明这个node接收中断信号;
    2. #interrupt-cells 这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符;
    3. interrupt-parent 标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的;
    4. interrupts 一个中断标识符列表,表示每一个中断输出信号。

    如果有两个,第一个是中断号,第二个是中断类型,如高电平、低电平、边缘触发等触发特性。对于给定的中断控制器,应该仔细阅读相关文档来确定其中断标识该如何解析。一般如下:

    二个cell的情况

    第一个值: 该中断位于他的中断控制器的索引;

    第二个值:触发的type

    固定的取值如下:

            1 = low-to-high edge triggered
            2 = high-to-low edge triggered
            4 = active high level-sensitive
            8 = active low level-sensitive


    三个cell的情况

    第一个值:中断号

    第二个值:触发的类型

    第三个值:优先级,0级是最高的,7级是最低的;其中0级的中断系统当做 FIQ处理。


    c. 其他

    除了以上规则外,也可以自己加一些自定义的属性和子节点,但是一定要符合以下的几个规则:

    1.    新的设备属性一定要以厂家名字做前缀,这样就可以避免他们会和当前的标准属性存在命名冲突问题;

    2.    新加的属性具体含义以及子节点必须加以文档描述,这样设备驱动开发者就知道怎么解释这些数据了。描述文档中必须特别说明compatible的value的意义,应该有什么属性,可以有哪个(些)子节点,以及这代表了什么设备。每个独立的compatible都应该由单独的解释。

    新添加的这些要发送到devicetree-discuss@lists.ozlabs.org邮件列表中进行review,并且检查是否会在将来引发其他的问题。

     

    DTS设备树描述文件中什么代表总线,什么代表设备

    一个含有compatible属性的节点就是一个设备。包含一组设备节点的父节点即为总线。

     

    由DTS到device_register的过程

    dts描述的设备树是如何通过register_device进行设备挂载的呢?我们来进行一下代码分析

     在arch/arm/mach-******/******.c找到DT_MACHINE_START 和 MACHINE_END 宏, 如下:

    [objc] view plain copy
    1. DT_MACHINE_START(******_DT, "************* SoC (Flattened DeviceTree)")  
    2.     .atag_offset    = 0x100,  
    3.     .dt_compat    =******_dt_compat,                // 匹配dts  
    4.     .map_io        =******_map_io,                   // 板级地址内存映射, linux mmu  
    5.     .init_irq    =irqchip_init,                    // 板级中断初始化.  
    6.     .init_time    =******_timer_and_clk_init,        // 板级时钟初始化,如ahb,apb等   
    7.     .init_machine   = ******_dt_init,              // 这里是解析dts文件入口.  
    8.     .restart    =******_restart,                  // 重启, 看门狗寄存器相关可以在这里设置  
    9. MACHINE_END  
    其中.dt_compat    = ******_dt_compat 这个结构体是匹配是哪个dts文件, 如:
    [objc] view plain copy
    1. static const charchar * constconst ******_dt_compat[] = {  
    2.     "******,******-soc",  
    3.     NULL  
    4. };  
    这个"******,******-soc" 字符串可以在我们的dts的根节点下可以找到.

    好了, 我们来看看init_machine   = ******_dt_init 这个回调函数.
    1. arch/arm/mach-******/******.c : void __init ******_dt_init(void)
        ******_dt_init(void) --> of_platform_populate(NULL,of_default_bus_match_table, NULL, NULL);
        of_default_bus_match_table 这个是structof_device_id的全局变量.
    [objc] view plain copy
    1. const struct of_device_id of_default_bus_match_table[] = {  
    2.      { .compatible = "simple-bus",},  
    3.  #ifdef CONFIG_ARM_AMBA  
    4.      { .compatible = "arm,amba-bus",},  
    5.  #endif /* CONFIG_ARM_AMBA */  
    6.      {} /* Empty terminated list */  
    7.  };  
     我们设计dts时, 把一些需要指定寄存器基地址的设备放到以compatible = "simple-bus"为匹配项的设备节点下. 下面会有介绍为什么.

    2. drivers/of/platform.c : int of_platform_populate(...)
        of_platform_populate(...) --> of_platform_bus_create(...)
        // 在这之前, 会有of_get_property(bus,"compatible", NULL) 
        // 检查是否有compatible, 如果没有, 返回, 继续下一个, 也就是说没有compatible, 这个设备不会被注册
    [objc] view plain copy
    1. for_each_child_of_node(root, child) {  
    2.     printk("[%s %s %d]child->name = %s, child->full_name = %s\n", __FILE__, __func__,__LINE__, child->name, child->full_name);  
    3.     rc = of_platform_bus_create(child,matches, lookup, parent, true);  
    4.     if (rc)  
    5.         break;  
    6. }  
        论询dts根节点下的子设备, 每个子设备都要of_platform_bus_create(...);
        全部完成后, 通过 of_node_put(root);释放根节点, 因为已经处理完毕;

    3. drivers/of/platform.c : of_platform_bus_create(bus, ...)
      
    [objc] view plain copy
    1. dev = of_platform_device_create_pdata(bus, bus_id,platform_data, parent); // 我们跳到 3-1步去运行  
    2.   if (!dev || !of_match_node(matches, bus))  // 就是匹配  
    3.                                             // dt_compat    = ******_dt_compat, 也就是 compatible = "simple-bus",   
    4.                                             // 如果匹配成功, 以本节点为父节点, 继续轮询本节点下的所有子节点  
    5.       return 0;  
    6.   
    7.   for_each_child_of_node(bus, child) {  
    8.       pr_debug("   create child:%s\n", child->full_name);  
    9.       rc = of_platform_bus_create(child,matches, lookup, &dev->dev, strict);  // dev->dev以本节点为父节点,  我们跳到 3-2-1步去运行  
    10.       if (rc) {  
    11.           of_node_put(child);  
    12.           break;  
    13.       }  
    14.   }  
    3-1. drivers/of/platform.c : of_platform_device_create_pdata(...)
    [objc] view plain copy
    1. if (!of_device_is_available(np))   // 查看节点是否有效, 如果节点有'status'属性, 必须是okay或者是ok, 才是有效, 没有'status'属性, 也有效  
    2.     return NULL;  
    3.   
    4. dev = of_device_alloc(np, bus_id, parent);  // alloc设备, 设备初始化. 返回dev, 所有的设备都可认为是platform_device, 跳到3-1-1看看函数做了什么事情  
    5. if (!dev)  
    6.     return NULL;  
    7.   
    8. #if defined(CONFIG_MICROBLAZE)  
    9.     dev->archdata.dma_mask = 0xffffffffUL;  
    10. #endif  
    11.     dev->dev.coherent_dma_mask =DMA_BIT_MASK(32); // dev->dev 是 struct device. 继续初始化  
    12.     dev->dev.bus =&platform_bus_type;     //   
    13.     dev->dev.platform_data =platform_data;  
    14.   
    15. printk("[%s %s %d] of_device_add(device register)np->name = %s\n", __FILE__, __func__, __LINE__, np->name);  
    16. if (of_device_add(dev) != 0){       // 注册device,of_device_add(...) --> device_add(...) // This is part 2 ofdevice_register()  
    17.     platform_device_put(dev);  
    18.     return NULL;  
    19. }  
    3-1-1. drivers/of/platform.c : of_device_alloc(...)
        1) alloc platform_device *dev
        2) 如果有reg和interrupts的相关属性, 运行of_address_to_resource 和of_irq_to_resource_table, 加入到dev->resource
    [objc] view plain copy
    1. dev->num_resources = num_reg +num_irq;  
    2. dev->resource = res;  
    3. for (i = 0; i < num_reg; i++, res++) {  
    4.     rc = of_address_to_resource(np,i, res);  
    5.     /*printk("[%s %s %d] res->name = %s, res->start = 0x%X, res->end =0x%X\n", __FILE__, __func__, __LINE__, res->name, res->start,res->end); */  
    6.     WARN_ON(rc);  
    7. }  
    8. WARN_ON(of_irq_to_resource_table(np, res,num_irq) != num_irq);  
        3) dev->dev.of_node = of_node_get(np);  
            // 这个node属性里有compatible属性, 这个属性从dts来, 后续driver匹配device时, 就是通过这一属性进匹配 
            // 我们可以通过添加下面一句话来查看compatible.
            // printk("[%s %s %d]bus->name = %s, of_get_property(...) = %s\n", __FILE__, __func__,__LINE__, np->name, (char*)of_get_property(np, "compatible",NULL));
            // node 再给dev, 后续给驱动注册使用.
        4) 运行 of_device_make_bus_id 设定device的名字, 如: soc.2 或 ac000000.serial 等

    3-2. drivers/of/platform.c : 
        以 compatible = "simple-bus"的节点的子节点都会以这个节点作为父节点在这步注册设备.

        至此从dts文件的解析到最终调用of_device_add进行设备注册的过程就比较清晰了。



    查看挂载上的所有设备

    cd /sys/devices/ 查看注册成功的设备  对应devicetree中的设备描述节点


    ##########################################################

      欢迎广大学子交流嵌入式和安卓开发

      aiku老师 微信号  :aiku868

       微信公众平台:aiku嵌入式视频教程创科之龙

       aiku老师QQ:1653687969

       技术解答QQ群:234945702


    ##########################################################



    展开全文
  • 搜索附近蓝牙设备并进行匹配

    千次阅读 2019-06-19 21:20:54
    其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建与其它人冲突的UUID。在这样的情况下,就需考虑数据库创建时的名称重复问题...

    第一步动态申请权限,有两条

    	<uses-permission android:name="android.permission.BLUETOOTH"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    

    申请过程

    implements EasyPermissions.PermissionCallbacks
    private  void initpers(){
            if(!EasyPermissions.hasPermissions(this,pers)){
                EasyPermissions.requestPermissions(this,"权限申请",RC_REQUEST_BLUETOOTH_NOR,pers);
            }
        }
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            EasyPermissions.onRequestPermissionsResult(requestCode,permissions,grantResults,this);
        }
    
        @Override
        public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
            toast("权限授予");
        }
    
        @Override
        public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
            toast("权限禁止");
        }
    

    申请完权限我们就可以开始干活了

    先初始化数据

    private void initView() {
            btn_start = findViewById(R.id.btn_start);
            btn_stop = findViewById(R.id.btn_stop);
            btn_search = findViewById(R.id.btn_search);
    
            btn_start.setOnClickListener(this);
            btn_stop.setOnClickListener(this);
            btn_search.setOnClickListener(this);
    
            listView = findViewById(R.id.listView);
            mainAdapter = new MainAdapter();
            listView.setAdapter(mainAdapter);
    
        }
     private void initDate() {
            bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
            bluetoothAdapter = bluetoothManager.getAdapter();//蓝牙adapter
            if (bluetoothAdapter == null) {
                finish();
                return;
            }
            bluetoothReceiver = new BluetoothReceiver();
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//可发现
            intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索结束
            intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//绑定状态
            registerReceiver(bluetoothReceiver, intentFilter);
        }
    

    然后设置功能(开启,关闭,搜索)

    @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_start:
                    boolean flag = bluetoothAdapter.enable();
                    if (!flag) {
                        Intent intent = new Intent();
                        intent.setAction(BluetoothAdapter.ACTION_REQUEST_ENABLE);//启动蓝牙
                        intent.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);//开启本设备可被发现
                        intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 200);//指定搜索设备时间
                        startActivityForResult(intent, 100);
                        bluetoothAdapter.startDiscovery();//开启搜索
                    }
                    break;
                case R.id.btn_stop:
                    bluetoothAdapter.disable();//关闭蓝牙
                    list.clear();
                    mainAdapter.refresh(list);
                    break;
                case R.id.btn_search:
                    bluetoothAdapter.startDiscovery();//开启搜索
                    break;
                default:
                    break;
    
            }
        }
    

    然后就是最后也是最终要的一步
    接收广播

    class MyReciver extends BroadcastReceiver{
    
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                switch (action){
                    case BluetoothDevice.ACTION_FOUND:
                        BluetoothDevice parcelableExtra = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        String address = parcelableExtra.getAddress();
                        String name = parcelableExtra.getName();
                        Log.i(TAG, "onReceive: "+name+"___"+address);
                        beans.add(new Bean(name,address));
                        devices.add(parcelableExtra);
                        myAdapter.fresh(beans);
                        break;
                    case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
    
                        device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        int state = device.getBondState();
                        if(state == BOND_NONE){
                            Log.i(TAG, "onReceive: 没有设备");
                        } else if (state == BOND_BONDING) {
                            Log.i(TAG, "onReceive: 正在匹配中");
                        } else if (state == BOND_BONDED) {
                            Log.i(TAG, "onReceive: 匹配成功");
                            ConnectManager connectManager = new ConnectManager(device);
    
                        }
                        break;
                    case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                        break;
                    default:
                        break;
                }
            }
        }
    

    这样最基本的搜索附近蓝牙

    在这样的基础上,我们需要一个可以与其他设备进行交互配对的方法
    来自Adapter的createBond方法
    来看一下
    在这里插入图片描述
    给列表添加条目点击监听,单独写一个集合用于存储搜索到的每一台设备,开个线程使其始终发生改变
    然后通过反射拿到次方法,也可以直接通过devic对象去调用也行,但是不如反射稳定
    ‘最后则是开启一个线程与对方进行交互’信息

    public class ConnectManager {
    //    使用传输服务,用uuid
        private String uuid = "00001101-0000-1000-8000-00805F9B34FB";
        private BluetoothDevice device;
    
        public ConnectManager(BluetoothDevice device) {
            this.device = device;
            new SendThread().start();
        }
    
        private BluetoothSocket socket;
    
        class SendThread extends Thread{
            @Override
            public void run() {
                super.run();
                try {
                    socket = device.createRfcommSocketToServiceRecord(UUID.fromString(uuid));
                    socket.connect();
                    OutputStream ops = socket.getOutputStream();
                    ops.write("蓝牙已连接".getBytes());
                    ops.flush();
                    ops.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    
    

    这里有使用到uuid,这是一个唯一的码,用以确定某种功能的(个人理解)再来个官方的

    UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则有Linux ext2/ext3文件系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可以在e2fsprogs包中的UUID库找到实现。

    交互也是通过socket和流实现的
    简单的通讯就可以了

    INTERESTING!!!

    展开全文
  • i2c设备与驱动匹配过程

    千次阅读 2017-09-13 19:01:15
    由 总线(bus_type) + 设备(device) + 驱动(device_driver) 组成,在该模型下,所有的设备通过总线连接起来,即使有些设备没有连接到一根物理总线上,linux为其设置了一个内部的、虚拟的platform总线,用...

    1. 几个基本概念

    1.1. 设备模型

    由 总线(bus_type) 设备(device) 驱动(device_driver) 组成,在该模型下,所有的设备通过总线连接起来,即使有些设备没有连接到一根物理总线上,linux为其设置了一个内部的、虚拟的platform总线,用以维持总线、驱动、设备的关系。

    因此,对于实现一个linux下的设备驱动,可以划分为两大步:

    1、设备注册;

    2、驱动注册。

     

    当然,其中还有一些细节问题:

    1、驱动的probe函数

    2、驱动和设备是怎么进行绑定的。

    1.2. i2c设备驱动的几个数据结构

     

    i2c_adapter

    每一个i2c_adapter对应一个物理上的i2c控制器,在i2c总线驱动probe函数中动态创建。通过i2c_add_adapter注册到i2c_core

     

    i2c_algorithm

    i2c_algorithm中的关键函数master_xfer(),以i2c_msg为单位产生i2c访问需要的信号。不同的平台所对应的master_xfer()是不同的,需要根据所用平台的硬件特性实现自己的xxx_xfer()方法以填充i2c_algorithmmaster_xfer指针;在A31上即是sun6i_i2c_algorithm函数。

     

    i2c_client

    代表一个挂载到i2c总线上的i2c从设备,包含该设备所需要的数据:

    i2c从设备所依附的i2c控制器 struct i2c_adapter *adapter

    i2c从设备的驱动程序struct i2c_driver *driver

    i2c从设备的访问地址addr, name

    i2c从设备的名称name

    2. i2c总线驱动

    2.1. 功能划分

    从硬件功能上可划分为:i2c控制器和i2c外设(从设备)。每个i2c控制器总线上都可以挂载多个i2c外设。Linux中对i2c控制器和外设分开管理:通过 i2c-sun6i.c 文件完成了i2c控制器的设备注册和驱动注册;通过i2c-core.c为具体的i2c外设提供了统一的设备注册接口和驱动注册接口,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器)。

    2.2. i2c-sun6i.c

    该文件是与具体硬件平台相关的,对应于A3x系列芯片。该文件实际上是i2c总线驱动的实现,本质上就是向内核注册i2c总线设备、注册总线驱动、实现总线传输的时序控制算法。i2c控制器被注册为Platform设备,如下:

        if (twi_used_mask & TWI0_USED_MASK)        platform_device_register(&sun6i_twi0_device);     if (twi_used_mask & TWI1_USED_MASK)        platform_device_register(&sun6i_twi1_device);     if (twi_used_mask & TWI2_USED_MASK)        platform_device_register(&sun6i_twi2_device);     if (twi_used_mask & TWI3_USED_MASK)        platform_device_register(&sun6i_twi3_device);     if (twi_used_mask)        return platform_driver_register(&sun6i_i2c_driver);
    

     

    需要注意的是:设备与驱动的对应关系是多对一的;即如果设备类型是一样的,会共用同一套驱动,因此上面代码只是注册了一次驱动platform_driver_register(&sun6i_i2c_driver) 

     

    设备注册:

    i2c控制器设备注册为platform设备,为每一个控制器定义一个struct platform_device数据结构,并且把.name都设置为"sun6i-i2c"(后面会通过名字进行匹配驱动的),然后是调用platform_device_register()将设备注册到platform bus上。

     

    设备注册完成后其直观的表现就是在文件系统下出现:/sys/bus/platform/devices/sun6i-i2c.0

     

    通过platform_device_register()进行的注册过程,说到底就是对struct platform_device这个数据结构的更改,逐步完成.dev.parent.dev.kobj.dev.bus的赋值,然后将.dev.kobj加入到platform_bus->kobj的链表上。

     

    驱动注册:

    步骤和设备注册的步骤类似,也是为驱动定义了一个数据结构:

    struct platform_driver sun6i_i2c_driver;

     

    因为一个驱动是可以对应多个设备的,而在系统里的3个控制器基本上是一致的(区别就是寄存器的地址不一样),所以上面注册的3个设备共享的是同一套驱动。

     

    设备与驱动匹配

    1.match过程

    i2c_add_driver-->i2c_register_driver-->i2c_bus_type-->.match->i2c_device_match-->of_driver_match_device/i2c_match_id(比较i2c_driver->id_table->name和client->name,如果相同,则匹配上,匹配上之后,运行driver_register调用driver_probe_device进行设备与驱动绑定。),

     

    2.probe过程

    初始化.probe.remove函数,然后调用i2c_add_driver进行驱动注册。主要函数调用流程:

    i2c_add_driver-->i2c_register_driver --> driver_register --> bus_add_driver --> driver_attach-->driver_probe_device-->really_probe(里面讲设备的驱动指针指向驱动,如果匹配成功,执行dev->bus->probe即设备驱动里的probe),-->driver_bound(绑定)

     

    需要注意的是driver_attach,这个函数遍历了总线上(platform_bus_type)的所有设备,寻找与驱动匹配的设备,并把满足条件的设备结构体上的驱动指针指向驱动,从而完成了驱动和设备的匹配(__driver_attach函数完成)。

     

    如果匹配到设备,这时就需要执行platform_bus_typeprobe函数,最终会调用设备驱动的probe函数(sun6i_i2c_probe)。

    2.2.1  sun6i_i2c_probe

    sun6i_i2c_probe函数中完成了大量的工作,包括硬件初始化、中断注册、为每个i2c控制器创建i2c_adapter等。

    1268         pdata = pdev->dev.platform_data;1269         if (pdata == NULL) {1270                 return -ENODEV;1271         }12721273         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);1274         irq = platform_get_irq(pdev, 0);1275         if (res == NULL || irq < 0) {1276                 return -ENODEV;1277         }12781279        if (!request_mem_region(res->start, resource_size(res), res->name)) {1280                 return -ENOMEM;1281         }
    

     

    • 首先得到当前设备的私有数据指针,并将其保留在pdata;进而通过platform_get_resource得到该设备占用的内存资源,并申请:request_mem_region。同时将irq资源也保留下来。 
    12881289         strlcpy(i2c->adap.name, "sun6i-i2c", sizeof(i2c->adap.name));1290         i2c->adap.owner   = THIS_MODULE;1291         i2c->adap.nr      = pdata->bus_num;1292         i2c->adap.retries = 3;1293         i2c->adap.timeout = 5*HZ;1294         i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;1295         i2c->bus_freq     = pdata->frequency;1296         i2c->irq                  = irq;1297         i2c->bus_num      = pdata->bus_num;1298         i2c->status       = I2C_XFER_IDLE;1299         i2c->suspended = 0;1300         spin_lock_init(&i2c->lock);1301         init_waitqueue_head(&i2c->wait);
    
    • 初始化i2c_adapter,并初始化一个工作队列 init_waitqueue_head 
    • 通过ioremap申请IO资源;
    • 通过request_irq申请irq资源,中断的处理服务函数是:sun6i_i2c_handler
    • sun6i_i2c_hw_init,对i2c控制进行硬件初始化;
    • i2c->adap.algo = &sun6i_i2c_algorithm,初始化控制器的总线传输算法,设备驱动调用;
    • 将初始化好的i2c_adapter注册到i2c_corei2c_add_numbered_adapter

    至此,probe函数完成。

    2.2.2  sun6i_i2c_core_process

    i2c控制器的中断服务程序sun6i_i2c_handler调用了sun6i_i2c_core_processi2c总线的实际传输控制也是在该函数里完成的。

    主要流程:

    1. 读取i2c控制器当前状态,twi_query_irq_status,保留在state中;
    2. 根据state的值进行分支跳转,控制i2c的工作状态;
    3. 传输完成,调用sun6i_i2c_xfer_complete,唤醒工作队列。

     

    2.2.3  sun6i_i2c_xfer

    每一个i2c控制器设备,在驱动绑定后,都会创建一个i2c_adapter,用以描述该控制器,i2c_adapter的建立与初始化是在驱动probe的时候建立的。每一个i2c_adapter包含了一个i2c_algorithm结构体的指针,i2c_algorithm是用来对外提供操作i2c控制器的函数接口的,主要是master_xfer函数,对应于i2c-sun6i.c,实际就是:

     

    static int sun6i_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    

    该函数的功能是通知i2c_adapter需要对外设进行数据交换,需要交换的信息通过struct i2c_msg *msgs传入。sun6i_i2c_xfer实际上是调用了sun6i_i2c_do_xfer进行传输。

    因为i2c总线读写速率有限,sun6i_i2c_do_xfer启动i2c传输后,通过wait_event_timeout进入休眠,直到中断唤醒或者超时;中断唤醒是由sun6i_i2c_xfer_complete完成的。

    3. i2c设备驱动

    3.1. 驱动注册

    i2c从设备的驱动注册,使用的是i2c-core.c提供的接口:i2c_register_driver;其调用如下:

     

    i2c_register_driver --> driver_register --> bus_add_driver

     

    bus_add_driver进行分析:

    • 关于device_driver数据结构的 struct driver_private *p

    设备驱动模型是通过kobject对设备驱动进行层次管理的,因此device_driver应该包含kobject成员,linux是将kobject包含在struct driver_private中,再在device_driver中包含struct driver_private;我们可以理解driver_privatedevice_driver的私有数据,由内核进行操作。

    struct driver_private 是在驱动注册的开始,动态申请,并初始化的。

    • klist_init(&priv->klist_devices, NULL, NULL);

    初始化设备链表,每一个与该驱动匹配的device都会添加到该链表下。

    • priv->kobj.kset = bus->p->drivers_kset;

    指定该驱动所属的kset

    • kobject_init_and_add

    初始化kobject,并将kobject添加到其对应的kset集合中(即bus->p->drivers_kset)。

    该函数最终是调用kobject_add_internalkobject添加到对应的kset中;需要主要的是,如果kobjectparent如果为NULL,在此会将其parent设置为所属kset集合的kobject

    parent = kobject_get(&kobj->kset->kobj)

     

    接下来是为kobject创建文件夹:create_dir(kobj);从而能从/sys/目录下显示。

    • driver_attach,将驱动和设备进行绑定

    将遍历总线上的设备链表,查找可以匹配的设备,并绑定。

    driver_attach -->  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

    将函数指针__driver_attach传入bus_for_each_dev,将每个查找得到的device进行驱动匹配。

     

    bus_for_each_dev

    遍历总线上的所有设备,因为总线上的设备都是bus->p->klist_devices链表上的一个节点,因此该函数其实就是对链表的遍历,具体可以参考klist

     

    __driver_attach(源码位置drivers/base/dd.c):

    进行设备和驱动匹配,如果匹配成功,尝试进行绑定。

     

    1. 首先进行匹配确认:driver_match_device(drv,  dev)

    调用关系: --> drv->bus->match --> i2c_device_match 

    -->  of_driver_match_device

      i2c_match_id

     

    可以看出,最终有两种方式进行驱动匹配查询:

    方法一:通过of_driver_match_device对比of_device_id

    方法二:通过i2c_match_id对比id_table

    方法二实际上就是对比

    i2c_driver->id_table->name client->name是否一致。

     

    2.如果匹配确认,进行驱动与设备绑定:driver_probe_device

    调用关系: driver_probe_device --> really_probe

     --> dev->bus->probe

        driver_bound

     

    really_probe中,首先将设备的驱动指针指向该驱动:dev->driver = drv

    对应于i2c_bus_typedev->bus->probe 即是:i2c_device_probe,最终调用驱动的probe函数。

     

    最后是driver_bound,将驱动与设备进行绑定:

    其实就是调用klist_add_tail:将设备节点添加到驱动的klist_devices;

     

    • 调用klist_add_tail,将被注册的驱动添加到总线的klist_drivers上;

    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

     

    •  module_add_driver(drv->owner,  drv)

    sysfs创建drivers目录

    3.2. 设备注册

    方式一:i2c设备动态发现注册

    i2c_register_driver的最后:

            INIT_LIST_HEAD(&driver->clients);                i2c_for_each_dev(driver, __process_new_driver);
    
    

     

    观察i2c_for_each_dev 

    int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *)){        int res;         mutex_lock(&core_lock);        res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);        mutex_unlock(&core_lock);         return res;}
    

     

    其实就是遍历i2c总线上的klist_devices链表,对得到的每一个device,执行__process_new_driver 

    跟踪 __process_new_driver --> i2c_do_add_adapter --> i2c_detect

    i2c_detect实现了i2c设备发现:在注册驱动后,通过i2c_detect检测是否有适合的设备连接在总线上。i2c_detect实现如下:

    • 在每一个adapter上遍历驱动给出的地址列表(address_list),由i2c_detect_address函数完成;最终会调用driver->detect(即设备驱动提供的设备发现函数);
    • 如果发现满足条件的设备,执行i2c_new_device,为设备建立i2c_client;并且将设备添加到i2c_bus_type->p->klist_devices链表上(device_register),通过bus_add_device函数完成,最后调用bus_probe_device,尝试绑定驱动。
    • client添加到驱动的设备链表上:list_add_tail(&client->detected, &driver->clients)

     

    方式二:i2c设备之静态注册

    Linux 3.3 提供了静态定义的方式来注册设备,接口原型:linux-3.3/drivers/i2c/i2c-boardinfo.c

    int __initi2c_register_board_info(int busnum,        struct i2c_board_info const *info, unsigned len)
    

    核心内容: 

    • 申请struct i2c_devinfo,用以描述一个i2c外设;
    • list_add_tail(&devinfo->list, &__i2c_board_list),将devinfo加入链表__i2c_board_list,以供后续查找;

    扫描__i2c_board_list,创建client

    i2c_register_board_info只是把设备描述符加入到了__i2c_board_list,并没有创建client,当调用i2c_register_adapter注册adapter时,会扫描__i2c_board_list,创建client;具体调用:

    i2c_register_adapter 

    --> i2c_scan_static_board_info 

    --> i2c_new_device 

    --> device_register

     

    在 i2c_new_device完成了client创建,以及设备注册device_register

     

    PS

    由上面的注册流程可知,i2c_register_board_info应该在i2c_register_adapter之前完成,否则__i2c_board_list中的节点不会被扫描到。

     

    总结:

    • 由上述分析可知,i2c设备驱动是通过i2c_register_driver注册的,i2c设备是通过i2c_new_device注册的,在最后,这两个函数都尝试进行驱动和设备绑定(driver_attachbus_probe_device);因此不管是先注册驱动还是先注册设备,最后都能够将合适的驱动和设备进行绑定。
    • 有两种方式进行设备注册:

    1、通过i2c_register_board_info,在系统启动之初静态地进行i2c设备注册(axp电源驱动就是这样做的);

    2、实现i2c设备驱动的detect函数,在驱动加载的时候动态检测创建设备,aw平台的触摸屏驱动gt82x.ko就是通过这种方式。

    • Linux是通过在驱动数据结构中内嵌kobjectkset,完成了设备驱动的层次管理的,理解kobjectkset对理解设备驱动模型很重要。

    4. i2c驱动架构图

     

     

    1、i2c_add_adapter

    2、i2c_new_device/i2c_register_board_info

    3、i2c_add_driver

    4、调用i2c bus中注册的match函数进行匹配

    5、调用platform bus中注册的match函数进行匹配

    6、i2cdev_attach_adapter

    展开全文
  • 1,有时候我们用adb工具去...提示的字面意思就是当前client版本40,与server端的版本不匹配当前版本过高引起的。 注意,这里的client就是你电脑已经安装的adb程序的版本,而server,也就是你的服务端,将要connect...
  • 如图,有些车型在诊断时候,会读出两个甚至多个VIN码。...只有在单个模块故障、电路损坏、设备升级情况下,才会导致VIN码一致。 VIN码一致会出现什么问题:一般来说,车辆使用过程中,不会感觉到什么问题,但是...
  • DTS设备树的匹配过程 一个dts文件确定一个项目,多个项目可以包含同一个dtsi文件。找到该项目对应的dts文件即找到了该设备树的根节点。 kernel\arch\arm\boot\dts\qcom\sdm630-mtp.dts /* Copyright (c) 2017, The ...
  •  这样游戏开发的时候就会出现这样的问题,windows下测试正常的游戏(包括mac下的iOS模拟器运行正常的游戏,因为Mac系统也是文件名大小写敏感的),放到实际设备上运行可能各种崩溃。其中十有八九是配置文件名和...
  • 转载自:... 问题描述 ========== SQL Server 2008 R2 SBS中小型企业版安装在Windows 2008 标准版失败,提示:操作系统不匹配 故障排查 ======== 您当前的SQL Server版本为SQL Server 2008 St
  • js判断当前设备: var sUserAgent = navigator.userAgent.toLowerCase(); var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";//判断是否为iPad var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone ...
  • 获取当前连接的蓝牙设备的名称

    千次阅读 2020-08-12 17:02:05
    首先需要在清单文件添加权限: <!-- 蓝牙权限 --> <uses-permission android:...获取当前连接蓝牙设备名称需要先获取已绑定或已匹配的蓝牙列表,然后再一个一个判断是否在连接状态,但是因为android现在将获
  • 模板匹配

    千次阅读 2015-07-25 20:18:18
    模板匹配是数字图像处理的重要组成部分之一。把不同传感器或同一传感器在不同时间、 不同成像条件下对同一景物获取的两幅或多幅图像在空间上对准,或根据已知模式到另一幅 图中寻找相应模式的处理方法就叫做模板...
  • javaweb:判断当前请求是否为移动设备访问

    万次阅读 多人点赞 2014-07-07 18:27:02
    这段时间都是在做pc端的业务,结果经理找到我说,可能...由于移动端和pc端还是稍微有些区别的,我的想法是首先应该判断当前请求是否为移动端,然后设一个标识到session中,然后就可以随便处理了。不管你是单独处理,还
  • 相信很多玩vmware的童鞋遇到过这样的问题 与vmx86 驱动版本不匹配: 期待xxo.o,当前ooo.x 你的驱动程序vmx86.sys 版本正确。请重新安装VMware Workstation。未能初始化监视器设备。 一般是以前安装过旧版本的没...
  • adb 连设备 终极解决办法

    万次阅读 多人点赞 2019-01-19 18:32:28
    提示的字面意思就是当前client版本40,与server端的版本不匹配当前版本过高引起的。注意,这里的client就是你电脑已经安装的adb程序的版本,而server,也就是你的服务端,将要connect的设备。...
  • 图像拼接 之 特征点匹配

    千次阅读 2019-01-29 22:28:55
    特征点匹配
  • https://hostcoz.com/151.html ... 用微博帐号登录出错了!对第三方应用进行授权时出现错误,请您联系第三方应用的开发者:XXX 或者稍后再试。 错误码:21322重定向地址不匹配 遇到错误就去看...
  • 模板匹配原理-1

    万次阅读 2014-06-25 08:47:58
    关于控制点片匹配的算法有很多种,从最基本的分类可以分为基于像元的点片匹配和基于特征的点片匹配,由于基于特征的算法难度较大,一般使用的都是基于像元的。  首先简单说明一下,点片匹配在数字图像处理中交模
  • IntentFilter 的匹配规则 action 的匹配规则 category 的匹配规则 data 的匹配规则 总结过滤规则 注意 ThanksIntentIntent 是一个消息传递对象,我们可以使用它启动其他应用组件完成特定的任务。我们可以通过
  • 字符串匹配 汇编

    千次阅读 2018-07-05 17:56:44
    1、题目要求查找匹配字符串——程序接收用户键入的一个关键字以及一个句 子。如果句子中包含关键字则显示‘Nomatch!’;如果句子中包含关键字则显示‘Match’,且把该字在句子中的位置用十六进制数显示出来。2、...
  • android BLE重复连接设备或者断开后马上...将处理方法交由app端来进行处理,发现第一次连接上很快就可以连上设备,当调用Gatt.disconnect(),断开设备后再次连接新设备的时候会发现很长时间连接上,亦或是你断开当前
  • 根据图像匹配实现鼠标自动点击

    千次阅读 2019-07-31 09:58:28
    今天做自动化时碰到IE里面套用Google,无法获得句柄和URL,只能用图像匹配的思想来完成自动点击,本程序用到了几个包和包在安装过程中遇到的麻烦再其他博客都写到了。 pip install Pillow pip install opencv_python...
  • 双目视觉立体匹配算法的研究

    千次阅读 2020-12-24 19:01:32
    双目视觉立体匹配算法的研究 摘要 立体视觉匹配技术是机器视觉领域里最重要的研究...全局立体匹配算法通过最小化全局能量函数得到最优估计视差值,全局算法得到的视差图错误率低,但是时间复杂度非常高,适合实时应用
  • 本人第一次使用HBuilder联安卓机调试自己写的demo,遇到了一些棘手的问题,解决后把它记录下来希望能帮助刚进入这...真机运行:检测到手机设备     解决办法: 1. 首先通过数据线连电脑的USB接口使你的androi...
  • 绝对干货~~学习Linux设备驱动开发的过程中自然会遇到字符设备驱动、平台设备驱动、设备驱动模型和sysfs等相关概念和技术。对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术...
  • 出现提示“采样率不匹配音频设备”问题呢,出现这个问题,一般都是你当前声卡设置的采样率与效果工程采样率不匹配造成的,我们就需要改变下效果工程采样率就可以了,当然也可以改声卡采样率来匹配效果工程,只不过...
  • ACL的匹配顺序

    千次阅读 2019-10-17 10:17:41
    注:转自华为官方文档,作它用 如上面的流程图所示,一条ACL由多条rule规则组成时,这些规则可能存在重复或矛盾的地方。例如,在一条ACL中先后配置以下两条规则: rule deny ip destination 10.1.0.0 0.0.255....
  • 自从发布了iPhone6和plus后,苹果手机的...现在我们需要使用苹果新发布的AutoLayout来匹配不同的界面布局。具体步骤如下: (1)在storyboard中,设计如下布局,在界面底部显示一行文字。 。 (2)然后分别在iPhone

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 168,128
精华内容 67,251
关键字:

当前设备不匹配