2009-12-03 09:55:00 bengold1979 阅读数 1693
  • 高通骁龙™开发者工具链介绍及性能优化

    在本次课程中,将会就高通骁龙™开发者工具链、异构并行计算SDK、功耗优化SDK和性能分析工具进行深入浅出的介绍,帮忙开发者实现更高效的图形渲染和更低功耗,从而设计出更具表现力的游戏画面和更加稳定流畅的高帧率游戏。

    828 人正在学习 去看看 CSDN讲师

    好长时间没有写日志了。主要是现在的工作内容受高通授权的法律约束,不能随便乱写了。不过自己的解决思路和分析方法还是可以自己总结一下。

    最近做的比较多的就是实现U盘功能。而U盘功能的驱动开发重点有两个:

    一、设备的枚举

    这个部分,我之前已经写过一篇日志加以说明。这里就不再详细阐述。

    二、SCSI指令处理

    对于U盘来说需要注意几个关键SCSI指令。

  1. 0x00指令,用于检测U盘介质是否准备好。
  2. 0x03指令,Request Sense指令用于查询指定的逻辑单元。
  3. 0x12指令,主机驱动用于查询设备配置信息的指令。
  4. 0x1a指令,查询设备参数指令。
  5. 0x23指令,读当前设备可能格式化的容量信息。
  6. 0x28指令,长度为10的读取当前设备指定地址数据的指令。
  7. 0x2a指令,长度为10的写数据到当前设备指定地址的指令。

    在一个USB Masstorage设备中只要支持以上几个常见命令,就可以了。下面选几个重要命令的表格图分别贴在后面,表格中具体各域的详细说明,可参考usb的bulk传输命令的规范文档。如USB2.0规范等文档。

 0x00指令格式

0x03指令格式

0x12指令格式

0x23指令格式

0x25指令格式

0x28指令格式

0x2a指令格式

2017-03-29 14:51:00 weixin_34254823 阅读数 29
  • 高通骁龙™开发者工具链介绍及性能优化

    在本次课程中,将会就高通骁龙™开发者工具链、异构并行计算SDK、功耗优化SDK和性能分析工具进行深入浅出的介绍,帮忙开发者实现更高效的图形渲染和更低功耗,从而设计出更具表现力的游戏画面和更加稳定流畅的高帧率游戏。

    828 人正在学习 去看看 CSDN讲师

本人才疏浅学,写一篇文档总结自己在msm8916平台上移植自己编写的简单的字符设备驱动开发的整个流程。这个小项目的主要功能是开发一个简单的APP,APP通过JNI去调用位于kernel的字符设备驱动。

APP的设计,开发平台Android Studio

主要的文件是下面的三个文件:

MainActivity.java文件的内容如下:

 1 package com.example.administrator.myled;
 2 
 3 import android.nfc.Tag;
 4 import android.support.v7.app.AppCompatActivity;
 5 import android.os.Bundle;
 6 import android.util.Log;
 7 import android.view.View;
 8 import android.widget.Button;
 9 import android.widget.Toast;
10 
11 
12 import com.zbahuang.led.lowlevel.LedNative;
13 
14 public class MainActivity extends AppCompatActivity implements View.OnClickListener {
15     private final static String TAG = "zbzhuang";
16     Button btn_led_on;
17     Button btn_led_off;
18     LedNative myled;
19 
20 
21     @Override
22     protected void onCreate(Bundle savedInstanceState) {
23         super.onCreate(savedInstanceState);
24         setContentView(R.layout.activity_main);
25 
26         initUI();
27 
28         myled = new LedNative();
29         myled.openDev();
30         Log.d(TAG,"app:open Dev");
31     }
32 
33     private void initUI() {
34         btn_led_on = (Button) findViewById(R.id.btn_led_on);
35         btn_led_on.setOnClickListener(this);
36 
37         btn_led_off = (Button) findViewById(R.id.btn_led_off);
38         btn_led_off.setOnClickListener(this);
39     }
40 
41     @Override
42     public void onClick(View v) {
43         switch (v.getId()){
44             case R.id.btn_led_on:
45                 Toast.makeText(MainActivity.this,"拉灯-->",Toast.LENGTH_SHORT).show();
46                 Log.d(TAG,"app:LED on");
47                 myled.devOn();
48                 break;
49             case R.id.btn_led_off:
50                 Toast.makeText(MainActivity.this,"灭灯-->",Toast.LENGTH_SHORT).show();
51                 Log.d(TAG,"app:LED off");
52                 myled.devOff();
53                 break;
54             default:
55                 break;
56         }
57 
58     }
59 
60     @Override
61     protected void onDestroy() {
62         super.onDestroy();
63         myled.closeDev();
64         Log.d(TAG,"app:close Dev");
65     }
66 }
LedNative.java文件的内容如下:
在这个文件中所声明的方法是在jni中实现的啊。使用了特殊的关键字表示该方法是在JNI当中实现的啊。
 1 package com.zbahuang.led.lowlevel;
 2 
 3 /**
 4  * Created by Administrator on 2017/3/29 0029.
 5  */
 6 
 7 public class LedNative {
 8 
 9     static {
10         System.loadLibrary("led_jni");
11     }
12 
13     public native int openDev();
14     public native int devOn();
15     public native int devOff();
16     public native int closeDev();
17 }
18 package com.zbahuang.led.lowlevel;
19 
20 /**
21  * Created by Administrator on 2017/3/29 0029.
22  */
23 
24 public class LedNative {
25 
26     static {
27         System.loadLibrary("led_jni");
28     }
29 
30     public native int openDev();
31     public native int devOn();
32     public native int devOff();
33     public native int closeDev();
34 }

activity_main.xml文件的内容如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     tools:context="com.example.administrator.myled.MainActivity">
 8 
 9 
10     <RelativeLayout
11         android:layout_width="394dp"
12         android:layout_height="520dp"
13         tools:layout_editor_absoluteX="-5dp"
14         tools:layout_editor_absoluteY="-10dp">
15 
16         <Button
17             android:id="@+id/btn_led_on"
18             android:layout_width="wrap_content"
19             android:layout_height="wrap_content"
20             android:layout_alignParentStart="true"
21             android:layout_alignParentTop="true"
22             android:layout_marginStart="56dp"
23             android:layout_marginTop="109dp"
24             android:text="拉灯" />
25 
26         <Button
27             android:id="@+id/btn_led_off"
28             android:layout_width="wrap_content"
29             android:layout_height="wrap_content"
30             android:layout_alignBaseline="@+id/btn_led_on"
31             android:layout_alignBottom="@+id/btn_led_on"
32             android:layout_marginStart="81dp"
33             android:layout_toEndOf="@+id/btn_led_on"
34             android:text="灭灯" />
35     </RelativeLayout>
36 </android.support.constraint.ConstraintLayout>

JNI的文件:

led_jni.cpp

  1 #include <sys/types.h>
  2 #include <sys/stat.h>
  3 #include <fcntl.h>
  4 #include <unistd.h>
  5 #include <errno.h>
  6 #include <string.h>
  7 
  8 
  9 #define LOG_TAG "zbzhuang"
 10 #include <utils/Log.h>
 11 
 12 #include "jni.h"
 13 
 14 
 15 
 16 
 17 static jint fd;
 18 
 19 
 20 static jint open_led(JNIEnv *env,jobject thiz)
 21 {
 22     ALOGD("JNI:-----------%s--------------",__FUNCTION__);
 23 
 24     fd = open("/dev/led1",O_RDWR);
 25     if(fd < 0){
 26         ALOGD("JNI:open error:%s\n",strerror(errno));
 27         return -1;
 28     }
 29 
 30     return 0;
 31 }
 32 
 33 
 34 static jint led_on(JNIEnv *env,jobject thiz)
 35 {
 36     ALOGD("JNI:-----------%s--------------",__FUNCTION__);
 37     jint ret ;
 38     jint on = 1;
 39 
 40     ret = write(fd,&on,4);
 41     if(ret < 0){
 42         ALOGD("JNI:write off error:%s\n",strerror(errno));
 43         return -1;
 44     }
 45 
 46 
 47     return 0;
 48 }
 49 
 50 
 51 static jint led_off(JNIEnv *env,jobject thiz)
 52 {
 53     ALOGD("JNI:-----------%s--------------",__FUNCTION__);
 54     jint ret;
 55     jint off = 0;
 56 
 57     ret = write(fd,&off,4);
 58     if(ret < 0){
 59         ALOGD("JNI:write off error:%s\n",strerror(errno));
 60         return -1;
 61     }
 62 
 63     return 0;
 64 }
 65 
 66 
 67 static jint close_led(JNIEnv *env,jobject thiz)
 68 {
 69     ALOGD("JNI:-----------%s--------------",__FUNCTION__);
 70     close(fd);
 71 
 72     return 0;
 73 }
 74 
 75 
 76 
 77 
 78 
 79 const JNINativeMethod  led_jni_methods[] = {
 80     {"openDev","()I",(void *)open_led},
 81     {"devOn","()I",(void *)led_on},
 82     {"devOff","()I",(void *)led_off},
 83     {"closeDev","()I",(void *)close_led},
 84 
 85 
 86 };
 87 
 88 
 89 
 90 
 91 
 92 
 93 
 94 jint JNI_OnLoad(JavaVM * vm,void * reserved)
 95 {
 96     JNIEnv *env = NULL;
 97     jint ret ;
 98 
 99     
100 
101     ALOGD("%s[%s:%d]JNI:--------------^_&--------------------\n",__func__,__FILE__,__LINE__);
102     ret = vm->GetEnv((void * *)&env,JNI_VERSION_1_4);
103     if(ret != JNI_OK){
104         ALOGE("JNI:vm->GetEnv error");
105         return -1;
106     }
107 
108     jclass clz = env->FindClass("com/zbahuang/led/lowlevel/LedNative");
109 
110     if(clz == NULL){
111         ALOGE("%s[%s:%d]JNI:env->FindClass error",__func__,__FILE__,__LINE__);
112         return -1;
113     }
114 
115     ret = env->RegisterNatives(clz,
116                 led_jni_methods,
117                 sizeof(led_jni_methods)/sizeof(led_jni_methods[0]));
118 
119     if(ret < 0){
120         ALOGE("%s[%s:%d]JNI:env->RegisterNatives error\n",__func__,__FILE__,__LINE__);
121         return -1;
122     }
123 
124 
125 
126     return JNI_VERSION_1_4;
127     
128 
129 
130 }

 

Android.mk

 1 LOCAL_PATH:= $(call my-dir)
 2 include $(CLEAR_VARS)
 3 
 4 LOCAL_MODULE_TAGS := optional
 5 
 6 LOCAL_MODULE:= libled_jni
 7 
 8 LOCAL_SRC_FILES:= \
 9   led_jni.cpp
10 
11 LOCAL_SHARED_LIBRARIES := \
12     libutils liblog
13 
14 LOCAL_C_INCLUDES += \
15     $(JNI_H_INCLUDE)
16 
17 include $(BUILD_SHARED_LIBRARY)

执行:       mmm mytest/led_jni/   之后会生成动态库放在  out/target/product/msm8916_64/obj/lib/libled_jni.so

将这个库推送到平板电脑就可以通过这个库去调用驱动。

 

 

 

 

内核的驱动文件:kernel/drivers/input/misc/led.c

  1 /*1. 头文件*/
  2 #include<linux/init.h>
  3 #include<linux/module.h>
  4 #include<linux/fs.h>
  5 #include<linux/device.h>
  6 #include<linux/slab.h>
  7 #include<linux/cdev.h>
  8 #include<asm/uaccess.h>
  9 #include<asm/io.h>
 10 
 11 static unsigned int led_major=0;
 12 volatile unsigned long *gpc0con = NULL;
 13 volatile unsigned long *gpc0dat = NULL;
 14 
 15 struct led_device{
 16     struct class *led_class ;    //表示一类设备, 存储某些信息
 17     struct device *led_device ;    //表示一个设备
 18     struct cdev  *led_cdev;
 19     unsigned int val;
 20 
 21 };
 22 
 23 struct led_device *s5pv_led_dev;
 24 
 25 
 26 /*可用于查询LED的状态*/
 27 static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *opps)
 28 {
 29     int ret;
 30 
 31     ret = copy_to_user(buf, &s5pv_led_dev->val, count);
 32     if(ret>0)
 33     {
 34         printk(KERN_ERR "zbzhuang### copy to user failed!\n");
 35         return ret;
 36     }
 37 
 38     printk(KERN_INFO "zbzhuang### val=%d\n", s5pv_led_dev->val);
 39     
 40     return ret?0:count;
 41 }
 42 static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *opps)
 43 {
 44     int ret;
 45 
 46     /*拷贝成功,返回0; 拷贝失败, 返回没有被拷贝的字节数*/
 47     ret = copy_from_user(&s5pv_led_dev->val, buf, count);
 48     if(ret>0)
 49     {
 50         printk(KERN_ERR "zbzhuang### copy from user failed!\n");
 51         return ret;
 52     }
 53 
 54     if(s5pv_led_dev->val)
 55     {
 56         /*点亮LED*/
 57         //*gpc0dat |= ((0x1<<3)|(0x1<<4));
 58         printk(KERN_ERR "zbzhuang### led on\n");
 59     }
 60     else
 61     {
 62         /*熄灭LED*/
 63 //        *gpc0dat &= ~((0x1<<3)|(0x1<<4));
 64         printk(KERN_ERR "zbzhuang### led off\n");
 65     }
 66     
 67     return ret?0:count;
 68 }
 69 
 70 
 71 
 72 static int led_open(struct inode *inode, struct file *file)
 73 {
 74 #if 0
 75     /*1. 将物理地址映射为虚拟地址*/
 76     gpc0con = ioremap(0xE0200060, 8);
 77     gpc0dat = gpc0con +1;
 78     
 79     /*2. 初始化GPC0_3,4引脚功能为输出*/
 80     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 81     *gpc0con |=  ((0x1<<12)|(0x1<<16));    
 82 #endif
 83     printk(KERN_ERR "zbzhuang### -----------%s-------------\n",__FUNCTION__);
 84     
 85     return 0;
 86 }
 87 
 88 static int led_close(struct inode *inode, struct file *file)
 89 {
 90 #if 0
 91 
 92     *gpc0con &= ~((0xf<<12)|(0xf<<16));
 93     iounmap(gpc0con);
 94 #endif
 95     printk(KERN_ERR "zbzhuang### ------------%s-----------------\n",__FUNCTION__);
 96 
 97     return 0;
 98     
 99 }
100 
101 /*硬件操作方法*/
102 struct file_operations led_fops={
103     .owner = THIS_MODULE,    //当前模块所用
104     .open  = led_open,
105     .write = led_write,
106     .read  = led_read,
107     .release = led_close,
108 
109 };
110 
111 static void setup_led_cdev(void)
112 {
113     /*1. 为cdev结构体分配空间*/
114     s5pv_led_dev->led_cdev = cdev_alloc();
115 
116     /*2. 初始化cdev结构体*/
117     cdev_init(s5pv_led_dev->led_cdev, &led_fops);
118 
119     /*3. 注册cdev,加载到内核哈希表中*/
120     cdev_add(s5pv_led_dev->led_cdev, MKDEV(led_major, 0), 1);
121 
122 }
123 
124 /*2. 实现模块加载函数*/
125 static int __init led_init(void)
126 {
127     dev_t devno;
128     int ret;
129     /*1. 新的申请主设备号的方法*/
130     if(led_major)
131     {
132         /*静态申请*/
133         devno = MKDEV(led_major, 0);
134         register_chrdev_region(devno, 1, "led");
135     }
136     else
137     {
138         /*动态申请*/
139         alloc_chrdev_region(&devno, 0, 1, "led");
140         led_major = MAJOR(devno);
141     }
142 
143     /*2. 为本地结构体分配空间*/
144 
145         /*
146         **    param1: 大小
147         **    param2:    标号: GFP_KERNEL--->表示如果分配不成功,则休眠
148         */
149     s5pv_led_dev = kmalloc(sizeof(struct led_device), GFP_KERNEL);
150     if (!s5pv_led_dev)
151     {
152         printk(KERN_ERR "zbzhuang NO memory for malloc!\n");
153         ret = -ENOMEM;
154         goto out_err_1;
155     }
156 
157     /*3. 构建struct cdev结构体*/
158     setup_led_cdev();
159             
160 
161     /*4. 创建设备文件*/
162     /*
163     **    param1:    struct class
164     **    param2:    父类, 一般为NULL
165     **    param3:    dev_t ---> 表示一个设备号, 是一个无符号32位整形
166     **                        其中高12位为主设备号, 低20为次设备号
167     **            如何操作设备号: MKDEV(major, minor)根据主设备号和次设备号组成一个设备号
168     */    
169 
170     s5pv_led_dev->led_class = class_create(THIS_MODULE, "led_class");
171     if (IS_ERR(s5pv_led_dev->led_class)) {
172         printk(KERN_ERR "zbzhuang class_create() failed for led_class\n");
173         ret = -EINVAL;
174         goto out_err_2;
175     }
176 
177     
178     s5pv_led_dev->led_device = device_create(s5pv_led_dev->led_class, NULL, MKDEV(led_major, 0), NULL, "led1");    // 创建设备文件/dev/led1
179     if (IS_ERR(s5pv_led_dev->led_device)) {
180         printk(KERN_ERR "zbzhuang device_create failed for led_device\n");
181         ret = -ENODEV;
182         goto out_err_3;
183     }
184 
185     return 0;
186 
187 out_err_3:
188     class_destroy(s5pv_led_dev->led_class);
189 
190 out_err_2:
191     cdev_del(s5pv_led_dev->led_cdev);
192     kfree(s5pv_led_dev);
193 
194 out_err_1:
195     unregister_chrdev_region(MKDEV(led_major, 0), 1);
196     return ret;
197     
198 }
199 
200 /*3. 实现模块卸载函数*/
201 static void __exit led_exit(void)
202 {
203     unregister_chrdev_region(MKDEV(led_major, 0), 1);
204     device_destroy(s5pv_led_dev->led_class, MKDEV(led_major, 0));
205     class_destroy(s5pv_led_dev->led_class);
206     cdev_del(s5pv_led_dev->led_cdev);
207     kfree(s5pv_led_dev);
208 }
209 
210 /*4. 模块许可声明*/
211 module_init(led_init);
212 module_exit(led_exit);
213 MODULE_LICENSE("GPL");

修改kconfig和makefile,在内核当中添加:

makefile

kconfig:

 

修改这个文件将驱动编译进内核:kernel/arch/arm64/configs/msm_defconfig  与msm-perf_deconfig

 

 之后使用adb工具将重新编译的boot.imge镜像重新烧录就可以了啊。

看看log的输出验证一下结果:

android studio查看log的结果:

adb查看log的结果:

 

 

 

从app到jni到kernel,整个调用的过程成功。

如果出现打开设备失败的话在system/core/rootdir/uevent.rc中添加设备节点的权限777即可。

 

2010-11-21 15:45:00 Am_111 阅读数 6462
  • 高通骁龙™开发者工具链介绍及性能优化

    在本次课程中,将会就高通骁龙™开发者工具链、异构并行计算SDK、功耗优化SDK和性能分析工具进行深入浅出的介绍,帮忙开发者实现更高效的图形渲染和更低功耗,从而设计出更具表现力的游戏画面和更加稳定流畅的高帧率游戏。

    828 人正在学习 去看看 CSDN讲师

 

音频设备接口包括PCM IIS AC97三大类

两种音频驱动框架: ALSA OSS

OSS包含DSPMIXER字符设备接口,完全使用文件操作

ALSACARD和组件(PCM,mixer等)为主线,在用户空间的变成中不适用文件接口,而是使用alsalib,而下文要介绍的没有使用ALSAlib,而是使用了OSS lib

     

接口芯片为PCM系列

Linux 2.6.26

ARM9 AT91

 

首先,说明ALSAdriver部分

一,基本方法:

       先说明整个ALSA的体系,如下图所示,

 

直接提供给用户空间操作的文件操作方法是由oss层提供的,包括pcm_oss.cmixer_oss.c,如果编写一个pcm的驱动话,是在ALSA_driver层,包括的文件为at91_pcmXXX.cpcmXXX.c,驱动编写的主要思想是,通过probe将一个新的snd_soc_codec *codec结构填充,然后调用snd_soc_new_pcms注册一系列的pcm接口,最后调用snd_soc_register_card 注册声卡

 

(假设走的是先注册驱动,在注册设备的流程,二者谁先谁后都一样,事实上先注册的确实是驱动)

a)      soc-core.c

首先注册了一个平台驱动,

static struct platform_driver soc_driver = {

.driver           = {

        .name            = "soc-audio",

},

.probe           = soc_probe,

.remove        = soc_remove,

.suspend       = soc_suspend,

.resume         = soc_resume,

};

如果在没有设备名为“soc-audio”的设备时,则不调用.Probe

b)     at91_pcmXXX.c

同样,在epayment_snd_init中,先申请一个SSC设备(申请涉及到atmel_ssc.o模块),接着注册了一个platform的设备,起名字为“soc-audio”,该平台设备的driver_dataepayment_snd_devdata指针,该私有数据指针为struct  snd_soc_device的指针,使能SSC时钟。

此时,由于platform总线下的设备和驱动名字匹配,则调用driverprobe方法。

c)      .probe=soc_probe

Struct snd_soc_device包含四个结构,

分别是

Struct snd_soc_machine

Struct snd_soc_platform

Struct snd_soc_codec_device

Struct snd_soc_codec_drvdata

Probe中调用的顺序为,

Soc_core(probe)

    àcpu  àplatform  àcodec

Machine probe null

Cpu_dai probe null

Codec_dev->probe pcmXXX probepcmXXX.c

Platform probe null

最后初始化一个工作,在进行closeshutdown)操作时调用,该工作是保证在结束播放后,延时一定时间,保证资源全部释放之后,在进行最终结束操作。

而对于PcmXXX.probe

该函数的主要工作就是分配一个新的snd_soc_codec *codec结构,然后通过它注册PCM接口和卡设备

具体来说,PcmXXX_init()codec的各个参量的初始化及其注册

Codec->private_data=pcmXXXpri,

Codec->dai=&pcmXXX_dai

Codec->num_dai=1

接着调用snd_soc_new_pcms注册一系列的pcm接口

调用snd_soc_register_card 注册声卡

上述两个操作都是soc-core的操作

 

d)     对于ALSAopen/write/read/ioctl(用于配置参数和prepare),如果驱动调用相关操作时,会大致根据  àcpu  àplatform  àcodec的顺序进行相关方法的调用,具体的如下,CPUmachine)包括cpu接口和codec接口。

 

以上就是简单的借用platform driver完成的pcm设备驱动程序框架。

 

二.Atmel_ssc.o

 首先在板级程序中通过调用at91_add_device_ssc添加了SSC设备,

其中

static struct platform_device at91sam9260_ssc_device = {

     .name = "ssc",

     .id = 0,

     .dev   = {

         .dma_mask       = &ssc_dmamask,

         .coherent_dma_mask   = DMA_BIT_MASK(32),

     },

     .resource = ssc_resources,

     .num_resources  = ARRAY_SIZE(ssc_resources),

};

其中resource包括irqiomem

atmel_ssc driver中,name均为ssc,于是调用driverprobe函数。

Probe函数中,申请ssc_device结构指针ssc,填充ssc,包括iomem,irq,clk,pdev等,最后将ssc添加到ssc_list中。

spin_lock(&user_lock);

list_add_tail(&ssc->list, &ssc_list);

spin_unlock(&user_lock);

这样就完成了driver的注册,此时ssc_list不为空,查看ssc_request函数知道,申请ssc设备就是遍历ssc_list,然后查找设备号是否一致,如果一致且设备无人使用,则返回该结构的指针,完成设备的申请过程,并且使能SSC时钟。

 

三.SOC-CORE

a)      snd_soc_new_pcms

对应于controlC0设备和PCMC0D0P设备

该函数首先创建一个新的声卡卡结构(调用snd_card_new),然后根据num_links,分别调用soc_new_pcm创建新的pcm接口

 

       ---àsnd_card_new  controlC0设备添加到card->devices

                     具体就是, codec->card = snd_card_new(idx, xid, codec->owner, 0);

(这里使用codec->owner计数的原因是,一个card可以对应多个pcm流,但是一个card不能被多个pcm流占用计数,所以编码器的owner即为cardowner

 

                                   ---àsnd_ctl_create

创建一个新的卡设备,nameCONTROL,定义相应的卡设备操作,这里指CONTROL设备的snd_device_ops

       static struct snd_device_ops ops = {

              .dev_free = snd_ctl_dev_free,

              .dev_register =    snd_ctl_dev_register,

              .dev_disconnect = snd_ctl_dev_disconnect,

       };   

snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops)

然后将该卡结构(通过dev->list)添加到card->devices队列中(实际注册这些设备时通过遍历card->devices,然后调用各自的dev_register方法,对自身注册)

 

                                   ---àsnd_info_card_create,创建一个卡的proc 文件

 

       ---àsoc_new_pcm  PCMC0D0P设备添加到card->devices

                     同样的,ret = soc_new_pcm(socdev, &machine->dai_link[i], i);

                            这里有两个新数据结构

                     一个是struct snd_soc_pcm_runtime *rtd(runtime data )

                            Struct snd_soc_pcm_runtime

{

       Struct snd_soc_dai_link *dai;

       Struct snd_soc_device *socdev;

}

                     一个是struct snd_pcm *pcm

                            Pcm->private_data=rtd;

                              

                                   ---àsnd_pcm_newpcm.c

snd_ctl_create,一样的生成一个PCM device

                            具体涉及如下:

                                   static struct snd_device_ops ops = {

                                          .dev_free = snd_pcm_dev_free,

                                          .dev_register =    snd_pcm_dev_register,

                                          .dev_disconnect = snd_pcm_dev_disconnect,

                                   };

snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops))

然后将该卡结构(通过dev->list)添加到card->devices队列中(实际注册这些设备时通过遍历card->devices,然后调用各自的dev_register方法,对自身注册)

 

                                   ---à snd_pcm_set_ops….., &soc_pcm_ops

                      Soc_pcm_opspcm相关操作的指针

 

                                   ---àsocdev->platform->pcm_new

                                          具体的函数为at91_pcm_new

该函数主要为DMA传输设置DAM MASK

和提前分配DMA buffer,包括playbakccapture      

                                   ---à最后,pcm->private_free=socdev->platform->pcm_free

 

b)      snd_soc_register_card

 

                            ---àsnd_card_register(codec->card)

 

                                          ----àsnd_device_register_all(card)

这里将card->devices中的device分别注册,调用各自的device_ops中的.dev_register进行注册,实际上只有两个设备,controlpcm.

 

一个是control设备controlC0

Control调用的是自身的snd_ctl_dev_register

注册control设备,Control设备有其相应的设备操作方法,PCM设备也有其相应的设备操作方法。 

             

一个是pcm设备PCMC0D0P,

PCM调用的是自身的snd_pcm_dev_register

设置PCM设备的名字,然后针对不同的设备用途,是playback流还是capture流,分别注册不同的操作方法,我们这里只有一个playback,所以只注册了playback的方法,cidx=0,函数的调用如下,

              1).snd_register_device_for_dev    

这里真正的调用device_create,通过pcm->device这一嵌套的device注册了设备(PCM

              2).snd_add_device_sysfs_file 添加设备信息到sysfs

              3).snd_pcm_timer_init

该部分创建了timer设备,具体见pcm_timer

              4)  

list_for_each_entry(notify, &snd_pcm_notify_list, list)

              notify->n_register(pcm);

正是在这里注册了/dev/dsp , /dev/audiosnd_pcm_notify_list的由来,具体看PCM_OSS.O模块

 

                                          ----choose_default_id(card)

 

                                          ----àinit_info_for_card(card) proc文件的初始化

                                         

                                          ----à snd_mixer_oss_notify_callback

由于定义了

#ifdefined(CONFIG_SND_MIXER_OSS)||defined(CONFIG_SND_MIXER_OSS_MODULE)

所以mixer_oss模块被调用 在这里注册了mixer这一设备。具体见mixer_oss模块

                                                                            

                            ---àsnd_soc_dapm_sys_add(socdev->dev) 添加电源管理

 

三.PCM_OSS模块   

 

dev/目录下,可以看到6个与audio有关的设备,包括

mixer,dsp,audio,controlC0,pcmC0D0P,timer

从上面的图可以直观的说明它们的关系,对dsp设备文件的操作(OSS层),最终是调用pcm.o提供的方法,同样,对mixer设备文件的操作最终调用的是control.o提供的方法。

alsa_pcm_oss_init中,先初始化dsp_map[i]全为0adsp_map[i]全为1default设置),检查合理,然后调用snd_pcm_notify(&snd_pcm_oss_notify, 0)

snd_pcm_notify 函数中,

snd_pcm_oss_notify加入到snd_pcm_notify_list中,即

       list_add_tail(&notify->list, &snd_pcm_notify_list);

然后遍历snd_pcm_devices,找出链表中的已经添加的PCM,然后调用notify中的.n_register进行注册,也即    

list_for_each_entry(pcm, &snd_pcm_devices, list)

notify->n_register(pcm);

init中是没有PCM接口的,所以为空,只有在添加了PCM接口后,才会有一个,那就是上面soc_core中所说的。

snd_soc_register_card中,最后创建/dev/dsp/dev/audio时,是通过遍历notify list,然后调用对应的n_register完成的。

list_for_each_entry(notify, &snd_pcm_notify_list, list)

notify->n_register(pcm);

在链表snd_pcm_notify_list中,只有一个notify,就是上面注册了的snd_pcm_oss_notify

snd_pcm_oss_notify.n_register实际调用的就是

Snd_pcm_oss_register_minor(struct snd_pcm *pcm)

具体的,

Snd_pcm_oss_register_minor---à Register_oss_dsp(pcm,0)----àsnd_register_oss_device

通过snd_register_oss_device,建立了最上层的文件操作接口,该文件就是/dev/dsp文件和/dev/audio文件

snd_register_oss_device的具体调用为

register_sound_special_device(f_ops, 3, carddev); dsp   

register_sound_special_device(f_ops, 4, carddev); audio

最终调用

sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,

                             name, S_IRUSR | S_IWUSR, dev);

sound_insert_unit 调用两个,

r = __sound_insert_unit(s, list, fops, index, low, top)插入到chains链表中和device_create创建设备和文件

最后adsp_map[0]!=pcm->device,前者为1,跳过最后snd_pcm_oss_proc_init(pcm),proc oss init

以上就是建立设备文件/dev/dsp,/dev/audio,并将它们链入Chains的过程。

      

四.mixer_OSS模块  

在初始化的过程中将snd_mixer_oss_notify_callback这一函数指针赋值为snd_mixer_oss_notify_handler,但是由于初始化过程中,snd_cards为空,所以未建立mixer设备文件,直到在最终的初始化后建立了MIXER设备

       soc_core中提到最后会通过调用snd_mixer_oss_notify_callback创建mixer设备文件。

具体的,就是在该snd_mixer_oss_notify_handler中,调用如下,

----àsnd_register_oss_device MIXER注册。

----àsnd_mixer_oss_build     建立oss mixer element,该函数又调用.

----àsnd_mixer_oss_build_input 该函数主要完成struct snd_mixer_oss_slot *rslot的填充,包括put_volumeget_volumeget_recsrc,put_recsrc.

       ----àsnd_mixer_oss_proc_init  初始化oss mixerproc文件.

       Mixer_oss的相关操作是建立在得到mixer这一个kcontrol结构指针的基础上的,因为PCM没有相应的control寄存器,所以具体的mixer_oss的相关操作没有实现(control的相关操作亦没有实现)

      

五.Pcm_timer.c timer.c

       a)

Timer接口  为支持声音的同步事件提供访问声卡上的定时器。

       Snd_pcm_timer_init函数,首先通过snd_timer_new创建一个timer设备(这里也定义了设备的相关方法),并且将其添加到Chains链表中,然后通过snd_device_registerChains链表中的timer设备找到,并调用它自身的.dev_register将自身注册。注册之后,就能在/dev/目下看到timer设备了。

       以上基本与前面提到的dsp/audiomixer 的注册一致。

       同时,在init函数中,做了对timer的填充和一些赋值操作,包括

       Timer->hw=snd_pcm_timer

       Subtream->timer=timer

       Timer->private_data=substream

       Timer->private_free=snd_pcm_timer_free

       snd_pcm_timer_free的定义如下,

       static struct snd_timer_hardware snd_pcm_timer =

{

       .flags =  SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,

       .resolution = 0,

       .ticks =  1,

       .c_resolution =    snd_pcm_timer_resolution,

       .start =   snd_pcm_timer_start,

       .stop =          snd_pcm_timer_stop,

};

这些参数在snd_timer_notify中被用到.

 

       b)

Timer.csnd_timer_notifyWrite操作时的.post_action中被调用到,

       首先被调用的是

       Timer->hw.c_resolution,即snd_pcm_timer_resolution,该函数的到runtimeresolution

       接下来就是

       list_for_each_entry(ti, &timer->active_list_head, active_list) {

              if (ti->ccallback)

                     ti->ccallback(ti, event, tstamp, resolution);

              list_for_each_entry(ts, &ti->slave_active_head, active_list)

                     if (ts->ccallback)

                            ts->ccallback(ts, event, tstamp, resolution);

       }

       实际中,我们编写的PCMXXX驱动的定时器链表timer->active_list_head为空,所以不执行就直接返回了。

       由于timer接口是为支持声音的同步事件提供访问声卡上的定时器,所以不提供也能正常使用。

      

六.Control.c

控制接口control对于许多开关(switch)和调节器(slider)而言应用相当广泛,它能从用户空间被存取。control的最主要用途是mixer,所有的mixer 元素基于control 内核API 实现,在ALSA 中,control snd_kcontrol结构体描述。

创建一个control,调用snd_ctl_add()snd_ctl_new1()这两个函数来完成,步骤为

snd_ctl_new1()函数用于创建一个snd_kcontrol 并返回其指针,snd_ctl_add()函数用于将创建的snd_kcontrol 添加到对应的card 中。

如果需要创建control,可以在pcmXXX.c中的pcm_probe中进行实现不过由于pcm没有支持的control寄存器,所以probe中也没有进行此操作。

      

七.电源管理

        Pcm驱动中也没有提供电源管理的control结构,例如在WM8731中就有相应的设置,这是由芯片决定的,

        wm8731_add_controls(codec)  添加controlcard->controls链表中,然后遍历链表将这些control接口全部注册。

        wm8731_add_widgets(codec)  添加dapm control

 

        至于设备的suspendresumepcmXXX虽然有此函数,不过可以不用实现,因为连接的串口处于suspend时,pcm也就没有数据处理的过程,这种从设备的形式的suspendresume就可以不用实现。

         Suspend过程包括两个方面,

         At91-ssc.c中的suspendresumecpu

ssc处于suspend的状态时,保存此时SSC相关寄存器的值,待resume被调用时恢复。这些寄存器包括,SR,CMR,RCMR,RFMR,TCMR,TFMR等。

         At91-pcm.c中的suspendresumeplatform,

         至于平台的suspend,设计的就是PDC。在suspend时,禁止DMA传输,同时将PDC相关寄存器,包括XPR,XCR,XNPR,XNCR保存,待resume时恢复,进行继续的传输过程。

 

八.ALSA中的链表结构

       ALSA中设计到很多的链表结构,理解这些链表能更好的理解ALSA

a)       card->devices

card->devices链表的建立方便了card相关设备的注册过程和设备的管理。通过这个链表,在注册设备的过程中,可以先将设备(包括设备编号,设备相应的操作指针等)添加进链表中,然后再遍历链表,各自的设备调用本身的注册函数将自身注册,完成card相关所有设备的注册过程。

b)      snd_pcm_devices

该链表结构则是为了将已经存在了的PCM接口链接到该链表上,方便pcm的管理

c)       snd_pcm_notify_list

此链表是为pcm注册的通用方法,如果只注册了一个snd_pcm_oss_notify

,则在遍历snd_pcm_devices时,查找到的pcm device均使用该notify.n_register 进行PCM的注册。

d)      card->controls

cadr->controls链表和card->devices链表类似,只不顾一个负责管设备,一个负责管控制接口,基本操作类似,不过PCM中没有相关寄存器,所以未应用.

e)       snd_control_ioctls

control有关的链表,如果在controlsnd_ctl_add则是将control添加到此链表中。

f)       timer->active_list_head

timer有关的链表

      

九.驱动中各个结构体和各个模块的关系

       a) soc_core所用到的各个结构体之间的关联图,可以说是体系中的CORE层。如下,

 

 

从上图中看,soc_core中多数函数以soc_device指针为函数参量的原因也很显然。

而遵循soc_core的调用关系,即cpu----àplatform----àcodec,在上述结构总很好的体现。

还有一个重要的结构就是runtime,该结构代表的是DAI runtime的信息。

 

b)上面的结构图是以snd_soc_device为主线的,即以设备驱动的创建过程分析所得。而下面的这个结构图,是以snd_pcm_substream为主线,主要是分析在openhw_paramsWriteread等操作中由substream得到所需结构的过程。

 

下一篇博客将介绍前面谈到的ALSA体系中,从内核调用到驱动的全过程。

 

 

 

 

 

 

 

 

 

 

 

2016-09-21 11:35:37 weijory 阅读数 1899
  • 高通骁龙™开发者工具链介绍及性能优化

    在本次课程中,将会就高通骁龙™开发者工具链、异构并行计算SDK、功耗优化SDK和性能分析工具进行深入浅出的介绍,帮忙开发者实现更高效的图形渲染和更低功耗,从而设计出更具表现力的游戏画面和更加稳定流畅的高帧率游戏。

    828 人正在学习 去看看 CSDN讲师

前言:

让大家初步了解对高通MSM8916平台的输入子系统的实现,给大家提供Dragon Board 410c平台开发输入系统设备的思路。(如:按键设备、触摸屏、轨迹球等)




在高通MSM8916平台中,具有触摸屏、轨迹球和简单按键功能,这些功能是由Android系统内中的驱动程序实现的,并且需要用户空间的内容来协助实现。


一、触摸屏驱动编写

高通MSM8916平台的触摸屏驱动程序的实现文件是drivers/input/touchscreen/synaptics_i2c_rmi4.c,此文件的核心是函数synaptics_ts_probe(),在该函数中需要进行触摸屏工作模式的初始化,对作为输出设备的触摸屏驱动在Linux平台下的设备名注册,同事初始化触摸时间触发时引起的中断操作。此函数的实现代码如下:

static int synaptics_rmi4_probe(

struct i2c_client *client,const struct i2c_device_id *dev_id)

{

int retval = 0;
unsigned char ii;
unsigned char attr_count;
struct synaptics_rmi4_f1a_handle *f1a;
struct synaptics_rmi4_fn *fhandler;
struct synaptics_rmi4_fn *next_fhandler;
struct synaptics_rmi4_data *rmi4_data;
struct synaptics_rmi4_device_info *rmi;
struct synaptics_rmi4_platform_data *platform_data =
client->dev.platform_data;
struct dentry *temp;

if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev,
"%s: SMBus byte data not supported\n",
__func__);
return -EIO;
}

if (client->dev.of_node) {
platform_data = devm_kzalloc(&client->dev,
sizeof(*platform_data),
GFP_KERNEL);
if (!platform_data) {
dev_err(&client->dev, "Failed to allocate memory\n");
return -ENOMEM;
}

retval = synaptics_rmi4_parse_dt(&client->dev, platform_data);
if (retval)
return retval;
} else {
platform_data = client->dev.platform_data;
}

if (!platform_data) {
dev_err(&client->dev,
"%s: No platform data found\n",
__func__);
return -EINVAL;
}

rmi4_data = kzalloc(sizeof(*rmi4_data) * 2, GFP_KERNEL);
if (!rmi4_data) {
dev_err(&client->dev,
"%s: Failed to alloc mem for rmi4_data\n",
__func__);
return -ENOMEM;
}

rmi = &(rmi4_data->rmi4_mod_info);

rmi4_data->input_dev = input_allocate_device();//创建设备
if (rmi4_data->input_dev == NULL) {
dev_err(&client->dev,
"%s: Failed to allocate input device\n",
__func__);
retval = -ENOMEM;
goto err_input_device;


rmi4_data->i2c_client = client;
rmi4_data->current_page = MASK_8BIT;
rmi4_data->board = platform_data;
rmi4_data->touch_stopped = false;
rmi4_data->sensor_sleep = false;
rmi4_data->irq_enabled = false;
rmi4_data->fw_updating = false;
rmi4_data->suspended = false;

rmi4_data->i2c_read = synaptics_rmi4_i2c_read;
rmi4_data->i2c_write = synaptics_rmi4_i2c_write;
rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
rmi4_data->reset_device = synaptics_rmi4_reset_device;

rmi4_data->flip_x = rmi4_data->board->x_flip;
rmi4_data->flip_y = rmi4_data->board->y_flip;

if (rmi4_data->board->fw_image_name)
snprintf(rmi4_data->fw_image_name, NAME_BUFFER_SIZE, "%s",
rmi4_data->board->fw_image_name);

rmi4_data->input_dev->name = DRIVER_NAME;
rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
rmi4_data->input_dev->id.bustype = BUS_I2C;
rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
rmi4_data->input_dev->dev.parent = &client->dev;
input_set_drvdata(rmi4_data->input_dev, rmi4_data);

set_bit(EV_SYN, rmi4_data->input_dev->evbit);
set_bit(EV_KEY, rmi4_data->input_dev->evbit);
set_bit(EV_ABS, rmi4_data->input_dev->evbit);
set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit);
set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit);

#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
#endif

retval = synaptics_rmi4_regulator_configure(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to configure regulators\n");
goto err_reg_configure;
}

retval = synaptics_rmi4_power_on(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to power on\n");
goto err_power_device;
}

retval = synaptics_rmi4_pinctrl_init(rmi4_data);
if (!retval && rmi4_data->ts_pinctrl) {
/*
* Pinctrl handle is optional. If pinctrl handle is found
* let pins to be configured in active state. If not found
* continue further without error
*/
if (pinctrl_select_state(rmi4_data->ts_pinctrl,
rmi4_data->pinctrl_state_active))
dev_err(&rmi4_data->i2c_client->dev,
"Can not select %s pinstate\n",
PINCTRL_STATE_ACTIVE);
}

retval = synaptics_rmi4_gpio_configure(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev, "Failed to configure gpios\n");
goto err_gpio_config;
}

init_waitqueue_head(&rmi4_data->wait);
mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));

INIT_LIST_HEAD(&rmi->support_fn_list);
mutex_init(&rmi->support_fn_list_mutex);


retval = synaptics_rmi4_query_device(rmi4_data);
if (retval < 0) {
dev_err(&client->dev,
"%s: Failed to query device\n",
__func__);
goto err_free_gpios;
}


if (platform_data->detect_device) {
retval = synaptics_rmi4_parse_dt_children(&client->dev,
platform_data, rmi4_data);
if (retval < 0)
dev_err(&client->dev,
"%s: Failed to parse device tree property\n",
__func__);
}

if (rmi4_data->board->disp_maxx)
rmi4_data->disp_maxx = rmi4_data->board->disp_maxx;
else
rmi4_data->disp_maxx = rmi4_data->sensor_max_x;

if (rmi4_data->board->disp_maxy)
rmi4_data->disp_maxy = rmi4_data->board->disp_maxy;
else
rmi4_data->disp_maxy = rmi4_data->sensor_max_y;

if (rmi4_data->board->disp_minx)
rmi4_data->disp_minx = rmi4_data->board->disp_minx;
else
rmi4_data->disp_minx = 0;


if (rmi4_data->board->disp_miny)
rmi4_data->disp_miny = rmi4_data->board->disp_miny;
else
rmi4_data->disp_miny = 0;


input_set_abs_params(rmi4_data->input_dev,
ABS_MT_POSITION_X, rmi4_data->disp_minx,
rmi4_data->disp_maxx, 0, 0);
input_set_abs_params(rmi4_data->input_dev,
ABS_MT_POSITION_Y, rmi4_data->disp_miny,
rmi4_data->disp_maxy, 0, 0);
input_set_abs_params(rmi4_data->input_dev,
ABS_PRESSURE, 0, 255, 0, 0);
#ifdef REPORT_2D_W
input_set_abs_params(rmi4_data->input_dev,
ABS_MT_TOUCH_MAJOR, 0,
rmi4_data->max_touch_width, 0, 0);
input_set_abs_params(rmi4_data->input_dev,
ABS_MT_TOUCH_MINOR, 0,
rmi4_data->max_touch_width, 0, 0);
#endif

#ifdef TYPE_B_PROTOCOL
input_mt_init_slots(rmi4_data->input_dev,
rmi4_data->num_of_fingers, 0);
#endif

i2c_set_clientdata(client, rmi4_data);

f1a = NULL;
mutex_lock(&rmi->support_fn_list_mutex);
if (!list_empty(&rmi->support_fn_list)) {
list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
f1a = fhandler->data;
}
}
mutex_unlock(&rmi->support_fn_list_mutex);

if (f1a) {
for (ii = 0; ii < f1a->valid_button_count; ii++) {
set_bit(f1a->button_map[ii],
rmi4_data->input_dev->keybit);
input_set_capability(rmi4_data->input_dev,
EV_KEY, f1a->button_map[ii]);
}
}

retval = input_register_device(rmi4_data->input_dev);
if (retval) {
dev_err(&client->dev,
"%s: Failed to register input device\n",
__func__);
goto err_register_input;
}

configure_sleep(rmi4_data);

if (!exp_fn_inited) {
mutex_init(&exp_fn_list_mutex);
INIT_LIST_HEAD(&exp_fn_list);
exp_fn_inited = 1;
}

rmi4_data->det_workqueue =
create_singlethread_workqueue("rmi_det_workqueue");
INIT_DELAYED_WORK(&rmi4_data->det_work,
synaptics_rmi4_detection_work);
queue_delayed_work(rmi4_data->det_workqueue,
&rmi4_data->det_work,
msecs_to_jiffies(EXP_FN_DET_INTERVAL));

rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio);

retval = request_threaded_irq(rmi4_data->irq, NULL,
synaptics_rmi4_irq, platform_data->irq_flags,
DRIVER_NAME, rmi4_data);
rmi4_data->irq_enabled = true;


if (retval < 0) {
dev_err(&client->dev,
"%s: Failed to create irq thread\n",
__func__);
goto err_enable_irq;
}

for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&client->dev.kobj,
&attrs[attr_count].attr);
if (retval < 0) {
dev_err(&client->dev,
"%s: Failed to create sysfs attributes\n",
__func__);
goto err_sysfs;
}
}

synaptics_rmi4_sensor_wake(rmi4_data);

retval = synaptics_rmi4_irq_enable(rmi4_data, true);
if (retval < 0) {
dev_err(&client->dev,
"%s: Failed to enable attention interrupt\n",
__func__);
goto err_sysfs;
}

synaptics_secure_touch_init(rmi4_data);
synaptics_secure_touch_stop(rmi4_data, 1);
retval = synaptics_rmi4_check_configuration(rmi4_data);
if (retval < 0) {
dev_err(&client->dev, "Failed to check configuration\n");
return retval;
}

return retval;

err_sysfs:
for (attr_count--; attr_count >= 0; attr_count--) {
sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
&attrs[attr_count].attr);
}
err_create_debugfs_file:
debugfs_remove_recursive(rmi4_data->dir);
err_create_debugfs_dir:
free_irq(rmi4_data->irq, rmi4_data);
err_enable_irq:
cancel_delayed_work_sync(&rmi4_data->det_work);
flush_workqueue(rmi4_data->det_workqueue);
destroy_workqueue(rmi4_data->det_workqueue);
input_unregister_device(rmi4_data->input_dev);

err_register_input:
mutex_lock(&rmi->support_fn_list_mutex);
if (!list_empty(&rmi->support_fn_list)) {
list_for_each_entry_safe(fhandler, next_fhandler,
&rmi->support_fn_list, link) {
if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
synaptics_rmi4_f1a_kfree(fhandler);
else {
kfree(fhandler->data);
kfree(fhandler->extra);
}
kfree(fhandler);
}
}
mutex_unlock(&rmi->support_fn_list_mutex);
err_free_gpios:
if (gpio_is_valid(rmi4_data->board->reset_gpio))
gpio_free(rmi4_data->board->reset_gpio);
if (gpio_is_valid(rmi4_data->board->irq_gpio))
gpio_free(rmi4_data->board->irq_gpio);
err_gpio_config:
if (rmi4_data->ts_pinctrl) {
if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
devm_pinctrl_put(rmi4_data->ts_pinctrl);
rmi4_data->ts_pinctrl = NULL;
} else {
retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
rmi4_data->pinctrl_state_release);
if (retval)
pr_err("failed to select release pinctrl state\n");
}
}
synaptics_rmi4_power_on(rmi4_data, false);
err_power_device:
synaptics_rmi4_regulator_configure(rmi4_data, false);
err_reg_configure:
input_free_device(rmi4_data->input_dev);
rmi4_data->input_dev = NULL;
err_input_device:
kfree(rmi4_data);

return retval;

}

在上述代码中,通过I2c_smb_read_byte_data()函数对其寄存器信息进行读取即可完成其事件信息的获取,也可以通过i2c_transfer完成对其寄存器信息的批量读取。


2015-10-27 10:03:12 yanleizhouqing 阅读数 3510
  • 高通骁龙™开发者工具链介绍及性能优化

    在本次课程中,将会就高通骁龙™开发者工具链、异构并行计算SDK、功耗优化SDK和性能分析工具进行深入浅出的介绍,帮忙开发者实现更高效的图形渲染和更低功耗,从而设计出更具表现力的游戏画面和更加稳定流畅的高帧率游戏。

    828 人正在学习 去看看 CSDN讲师

很久没写日志,主要最近工作比较繁忙,最近主要做以太网开发的。

平台: 高通 ,android 4.3 ,linux 3.4.0

这一部分,主要说一下,调试过程中用到的一些命令。以太网,是由usb转net出来的,主要在在deconfig中配出usb_net这个配置就可以。

下面主要说的一些调试命令:

1. netcfg 查看当前的网卡设备名及其相关信息:

设备名       状态                                    IP                           MAC
lo          UP                                     127.0.0.1/8       0x00000049  00:00:00:00:00:00
sit0        DOWN                                   0.0.0.0/0     0x00000080      00:00:00:00:00:00
p2p0        UP                                     0.0.0.0/0          0x00001003 00:0a:f5:8b:fb:39
wlan0       UP                                     192.168.3.112/24  0x00001043  00:0a:f5:8b:fb:38    (wifi)


2. netcfg 动态连接网络:

netcfg eth0 dhcp up   给eth0动态分配IP、网关

一般这里,调用netcfg 查看信息,看是否分配出ip,若成功,则ping 192.168.3.0(网关),然后ping 114.114.114.114,若都能成功,则表示你的网络是没有问题,此时你的设备无法通过浏览器上网,这需要调通framework层。

3. netcfg 静态连接网络:

netcfg eth0 down

netcfg eth0 192.168.3.123 netmask 255.255.255.0 up  设置IP

route add default gw 192.168.3.1 dev eth0  设置网关

setprop net.dns1 192.168.3.1 设置DNS

一般到这里,应该能ping通网关并且能ping通百度。


没有更多推荐了,返回首页