- 博客(201)
- 收藏
- 关注
原创 嵌入式音频开发(4)- AudioFramework(java)核心功能
我们的自己开发的模块如果需要跟踪系统中音频设备的插拔情况,就可以通过实现AudioDeviceCallback 接口并注册到AudioManager中。这样当系统中的音频设备发生插拔时就会回调AudioDeviceCallback 中的钩子函数。注意:音频焦点是只存在Android java framework层的机制,在native framework并不存在相关的概念。4.通常焦点申请者会获得音频焦点,释放先前持有焦点的申请。(先处理失去焦点要做的逻辑,再处理新的申请者获得焦点的逻辑)
2025-08-23 08:59:36
431
原创 TRIZ(6)——系统功能分析与系统裁剪
按照功能的效果与期望之间的差异可将功能分为有用功能和负面功能,其中负面功能又可分为有害功能、不足功能以及过度功能。有用功能:指功能载体对功能对象的作用按照期望的方向改变功能对象的参数。负面功能:指功能载体对功能对象的作用不按照期望的方向改变功能对象的参数。负面功能主要有以下3种(1)有害功能:指功能载体对功能对象产生了有害的作用。(2)不足功能:指功能载体对功能对象的作用产生的实际改善值小于期望的改善值。
2025-08-23 08:58:53
868
原创 设备文件节点的生成
在Linux系统下,设备文件是种特殊的文件类型,其存在的主要意义是用户空间程序和内核空间驱动程序。换句话说,用户空间的应用程序要想使用驱动程序提供的服务,需要经过设备文件来达成。当然,如果驱动程序只是为内核中的其他模块提供服务,则没有必要生成对应的设备文件。按照通用的规则,Linux系统所有的设备文件都位于/dev目录下。/dev目录在Linux系统中算是一个比较特殊的目录,在Linux系统早期还不支持动态生成设备节点时,/dev目录就是挂载的根文件系统下的/dev,对这个目录下所有文件的操作使用的是。
2025-08-22 09:17:37
556
原创 TRIZ(6)——S曲线及技术系统进化法则
技术系统进化遵循S曲线规律,包括婴儿期、成长期、成熟期和衰退期四个阶段。技术系统通过高级发明实现快速发展,再通过低级发明逐步完善。其进化遵循完备性、能量传递和协调性三大生存法则,以及提高理想度、子系统不均衡进化、向超系统进化和向微观级进化等发展法则。技术系统在进化过程中不断提升理想度,最终趋向更高级别的系统。这些法则和规律可应用于技术预测和创新指导,具有广泛的应用前景。
2025-08-22 09:17:04
857
原创 Linux跨进程通信(IPC)
机制 效率 同步支持 适用场景管道 低 否 简单父子进程通信消息队列 中 否 结构化数据传递共享内存 极高 需外配 大数据量、高频交换信号量 高 是 进程同步与互斥。
2025-08-21 09:06:22
459
原创 嵌入式音频开发(2)- AudioService初始化
要想不修改AudioService的文件换启动方式的话,也可以不动Lifecycle的代码,只要不启动Lifecycle而采用其他方式实例化AudioService就行了。并在onstart函数中完成AudioService的发布(这步完成后,其它进程就可以获取AudioService的binder句柄并藉此使用AudioService提供的服务)。通过上面的内容,我们已经知道audioservice是怎么被实例化的,接下来我们看看audioservice在构造函数中做了哪些工作。
2025-08-21 09:05:51
1158
原创 嵌入式音频开发(1)-音频系统概览
audioflinger要负责具体的和硬件抽象层(hal)打交道,负责数据流的传递,混音,音量处理等工作。这些服务直接基于linux用户态相关的框架开发,提供媒体服务的支持。hal层:android定义的硬件抽象层,用于抽象底层的硬件结构。相关的API, 这些API既有java的版本,也有通过C++实现的。相关的API, 这些API既有java的版本,也有通过C++实现的(audiosystem)。这是任何基于linux内核开发的操作系统的基础,负责管理以及抽象硬件资源等传统意义上的操作系统的职责。
2025-08-20 18:12:59
592
原创 嵌入式音频开发(3)- AudioService核心功能
摘要:本文分析了Android系统中音量调节的核心实现逻辑,重点解读了AudioService中的adjustSuggestedStreamVolume和adjustStreamVolume两个关键函数。adjustSuggestedStreamVolume负责处理外部音量控制器、流类型判断等预处理逻辑;adjustStreamVolume则执行具体的音量调整操作,包含权限检查、静音处理等安全机制。文章通过代码注释的方式,揭示了TV/STB设备通过遥控器进行音量调节时,系统底层的处理流程和关键控制点。
2025-08-20 17:58:18
1379
原创 TRIZ(5)——发明原理(3)
在发生相变时,有体积的变化也有热量的吸收或释放,这类相变即称为“一级相变”(例如:在1个大气压0℃的情况下,1kg质量的冰转变成同温度的水,要吸收79.6Cal的热量,与此同时体积亦收缩。所以,冰与水之间的转换属于一级相变)。在发生相变时,体积不变且没有热量的吸收和释放,只是热容量、热膨胀系数和等温压缩系数等物理量发生变化,这类变化称为二级相变(例如:正常液态氦与超流氦之间的转变、正常导体与超导体之间的转变、顺磁体与铁磁体之间的转变、合金的有序态与无序态之间的转变等都是典型的二级相变的例子)。
2025-07-28 20:21:23
1279
原创 TRIZ(4)——发明原理(2)
普通集装箱提供了足够的空间,为运输的标准化作出了重要的贡献,但其体积庞大,在不用时非常浪费空间,是其最大的弊端之一。如果对象已经处于振动状态,则提高振动的频率(直至超高频): 超声波清洗机在将高频电能转换成机械能之后,会产生振幅极小的高频震动并传播到清洗槽内的溶液中,清洗液的内部将不断地产生大量微小的气泡并瞬间破裂,从而将工件冲刷干净。利用简易的廉价复制品,代替难以获得的、复杂的、昂贵的、不便于操作的或者易损易碎的物体: 服装店里的塑料模特(代替真人模特),或者大厅摆放的塑料花、塑料水果。
2025-07-28 20:20:57
622
原创 总线、设备与驱动(4)
需要注意的是,在注销一个驱动对象的过程中,如果其所在的总线定义了remove方法,那么内核会调用它,否则要看驱动所在的驱动程序中有没有实现该方法,如果实现了的话内核会调用该函数。函数中对kset_create_and_add(“class”, NULL, NULL)的调用将导致在/sys目录下新生成一个“class”目录(/sys/class),在以后的class相关的操作中,class_kset将作为系统中所有class内核对象的顶层kset。如果查找成功,将返回该驱动对象的指针,否则返回0。
2025-07-25 10:09:23
675
原创 TRIZ(3)——发明原理
提取工程参数()是为了将具体问题转化为典型问题,进而找出典型问题所对应的典型解决方案。绝大多数专利都是在解决矛盾,而且相似的矛盾之间,其解决方案在本质上也具有一致性。TRIZ理论从大量发明方案中总结、提炼出解决矛盾的40个发明原理。这也是在TRIZ理论发展过程中,阿奇舒勒最先得到的“解决问题的规律”。他发现,虽然不同的专利解决的是不同领域内的问题,但是它们使用的方法是具有相似性的,即一种方法可以解决来自不同工程技术领域的类似问题。
2025-07-25 10:08:52
605
原创 总线、设备与驱动(3)
由于在对dev对象调用device_initialize函数时,曾指定了dev所属的kset为devices_kset:dev->kobj.kset = devices_kset,所以这种情况下在将dev->kobj加入系统时,内核会将devices_kset所对应的kobj设置为dev->kobj的parent,所以dev->kobj.parent = devices_kset->kobj。最后的driver_bound(dev)用来将驱动程序的一些数据信息加入到dev对象中。
2025-07-24 11:53:40
1118
原创 linux驱动开发(22)-Linux设备驱动模型(三)
其次,call_usermodehelper_exec函数通过引入一个completion变量done来实现和工作节点sub_info->work上的延迟函数__call_usermodehelper的同步:函数通过queue_work(khelper_wq,&sub_info->work)将工作节点提交到khelper_wq队列之后,将等待在wait_for_completion(&done)语句上。__call_usermodehelper是该工作节点上的延迟执行的函数。直接看看核心的代码。
2025-07-24 11:52:52
626
原创 linux驱动开发(22)-Linux设备驱动模型(二)
其中kset_init和kobject_add_internal的功能都比较直观,分别用来初始化kset对象和向系统注册该kset对象,因为kset对象本身就是一个由kobject代表的内核对象,所以kobject_add_internal函数会为代表该kset对象的k->kobj在sysfs文件树中生成一个新目录,这个过程同前面谈到的kobject的操作是完全一样的。udev的实现基于内核中的。这些枚举数值定义了kset对象的一些状态变化,此处使用的是KOBJ_ADD,表明将向系统添加一个kset对象。
2025-07-23 09:33:24
585
原创 linux驱动开发(21)-Linux设备驱动模型(一)
sysfs_get_sb函数用来产生sysfs文件系统的超级块,其内部调用的最主要的函数是sysfs_fill_super,后者再经过一系列的函数调用链进入到sysfs_init_inode函数,这里之所以重点强调这个函数,是因为在接下来谈到内核对象的属性问题时会看到用户空间和内核对象的沟通问题,这种文件接口形式的交互发生在内核空间和用户空间,所以我们需要知道这条沟通的通道是如何建立起来的。显然不同类型的内核对象会有不同的ktype,用以体现kobject所代表的内核对象的特质。用来表示该内核对象的名称。
2025-07-23 09:33:06
712
原创 总线、设备与驱动(2)
在总线上发生的两类事件将导致设备与驱动绑定行为的发生:一是通过device_register函数向某一bus上注册一设备,这种情况下内核除了将该设备加入到bus上的设备链表的尾端,同时会试图将此设备与总线上的所有驱动对象进行绑定操作(当然,操作归操作,能否成功则是另外一回事);每类对应一个内核对象,分别为sysfs_dev_block_kobj和sysfs_dev_char_kobj,自然地这些内核对象也在sysfs文件树中占有对应的入口点,block和char内核对象的上级内核对象为dev_kobj。
2025-07-22 10:13:01
881
原创 总线、设备与驱动(1)
现在开始讨论Linux设备驱动模型的高层部分,核心分为三个组件,分别是总线(bus)、设备(device)和驱动(driver)。接下来将依次讨论每个组件,看看Linux引入的这个新的设备模型到底给系统带来了哪些好处和不足。
2025-07-22 10:12:38
846
原创 linux驱动开发(20)-DMA(四)
函数首先确定当前DMA池分配的对齐指标。函数通过dma_map_page来映射scatterlist上的page_link,跟x86平台不一样的是,ARM架构需要通过软件来保证cache一致性问题,所以做完这种虚拟物理地址的转换之后,ARM需要做的是使映射区对应的cache无效,以保证设备通过DMA将数据放到主存之后,CPU读到的不是cache中的数据,或者是保证CPU写到RAM中的数据立刻反映到RAM中,而不是暂时缓存到cache中,这样后续DMA在把主存中的数据传到设备中时,才能确保数据的有效性。
2025-06-25 09:34:11
800
原创 linux驱动开发(19)-DMA(三)
如同建立一致性映射的dma_alloc_coherent函数一样,dma_map_single内部用来完成实际的流式映射操作的代码也是体系架构相关的,内核通过struct dma_map_ops对象来屏蔽这种平台的差异,当然具体的平台需要提供其特有的struct dma_map_ops对象来供内核中的DMA层使用。的指针,cpu_addr是CPU的虚拟地址,也是流式映射DMA需要映射的区域,参数size指明了当前流式映射的空间范围,参数dir则用于表明当前的流式映射中DMA传输通道中的数据流向。
2025-06-25 09:33:51
769
原创 linux驱动开发(18)-DMA(二)
比如在该图中,如果RAM与Device之间的一次数据交换改变了RAM中DMA缓冲区的内容,假设在这个案例里恰好cache中缓存了DMA缓冲区对应的RAM中一段内存块,如果没有一种机制确保cache中的内容被新的DMA缓冲区数据所更新(或者无效),那么很明显cache和它对应的RAM中的一段内存块在内容上出现了不一致性。另外,这种一致性映射所获得的DMA缓冲区的大小都是页面的整数倍,如果驱动程序需要更小的一致性DMA缓冲区,则应该使用内核提供的DMA池(pool)机制,稍后讨论DMA缓冲池。
2025-06-24 09:17:12
1309
原创 软件设计与软件工程(7)-需求分析
目标要反映关键利益方的诉求需求不是“业务方告诉我怎么做,我就怎么做”,而是要理解“为什么做这件事情”。正如福特汽车公司的创办者亨利·福特的名言:(在汽车发明之前)如果你问客户需要的是什么,客户会告诉你,我需要的是一匹“更快的马”。理解需求背后的业务目标非常重要。在上面的名言中,受限于认知水平,客户给出的并不是最恰当的解决方案—毕竟客户没有见过汽车,只知道骑马这样一种在当时相对较好的交通方式。但是,客户的诉求—能很快抵达目的地则是比较确定的。
2025-06-24 09:16:52
804
原创 软件设计与软件工程(6)-优秀代码
刻板遵循设计范例的现象比较常见,例如,我还见过某个团队,因为设计一般是分成若干层,如三层架构的Controller、Service和DAO,所以即使自己的项目不是一个典型的数据库相关的项目,也要硬性分出这些层次,其实每个类都只是简单地向下一级传递了一次调用而已,这就属于典型的多余设计。这是一个非常有效的技巧,之所以有效,是因为:编写良好的自动化测试代码,是最好的产品文档。更进一步,几乎所有的规范项目,都应用了版本管理系统,如git工具,因为有这些工具的存在,所以任何历史上曾经存在的代码,都可以方便地找回。
2025-06-23 09:13:07
733
原创 软件设计与软件工程(5)-优秀代码
下图展示了几种不同情况下的依赖示例。图中的每一个圆圈都代表一个设计单元,它可能是类,也可能是模块或者系统。箭头代表设计单元之间的某种依赖关系,如A使用了B的方法或者数据,甚至A只是简单地共享了B的知识,那么A就依赖于B。(不要将依赖局限于调用,若B改变则A也必须改变。那么A就依赖B)下面分别介绍这四种依赖形态对耦合的影响。(1) 循环依赖造成紧耦合。图中的①表示A和B之间存在循环依赖。循环依赖是一种非常紧的耦合。因为A的变化会引起B的变化,B的变化也会引起A的变化,所以A和B本质上是一个整体,而不是两个不同
2025-06-23 09:12:40
961
原创 软件设计与软件工程(4)-优秀代码
在我们的开发过程中会遇到不同类型的 依赖关系,相应的,这些依赖关系都有对应的解决方案来降低耦合度。登录逻辑的变化频率一般较低,但是消费记录的逻辑,以及消费记录的展示是否要和登录动作放在一起都有更多变化的可能。.从易于复用的角度看,登录功能本来是一个通用资产,可在各种场景下使用,但是加入了和消费记录相关的信息后,就只能在本系统中使用了。尽管在某个特定的业务场景下,消费记录和用户的登录动作会被组合,但是从概念上看,这样的设计是不内聚的,它们至多是相关的概念,很难说是“紧密相关”。其实,这是没有唯一答案的。
2025-06-22 08:16:54
888
原创 软件设计与软件工程(3)-优秀代码
风格一致是对代码最基本的要求,门槛不高。在养成好的编程习惯、建立好的编程规范之后,风格一致是很容易做到的。形象地说,风格一致指的是“代码看上去像同一个人写出来的”。(即使是这么简单,规则明确的要求在很多情况下都做不到,因为不少代码的committer并不重视这点)代码是软件开发活动中最基本的信息媒介,也是软件存在最重要的载体。没有人希望看到一段代码是一种布局,换一段代码就变成了另外一种布局。也没有人希望看到在一个地方是一种异常处理方式,在另外一个地方又是另一种。类似的情况还有很多。(其实背后蕴藏这破窗效应
2025-06-22 08:16:32
993
原创 软件设计与软件工程(2)-优秀代码
通过恰当的确定问题域的边界,如把一个订餐系统切分为用户、订单、支付、配送、消息通知等子域,并保持各个子域边界之间的抽象和隔离,就可以大幅提升问题的通用性,从而增加复用机会。此外,通过恰当的抽象,让代码在面临新的业务场景时更容易扩展,甚至完全无须修改原来的代码,这就是面向对象设计中的开放 - 封闭原则。尽管软件行业在框架层面的复用已经取得了突出的进步,如平台级的k8s1、框架级的Spring2等,但是在业务层面,还有许多业务组件都必须从头写起,很难在不同的场景下复用。,是代码质量的基础,也是代码演进的基础。
2025-06-20 08:41:33
1224
原创 软件设计与软件工程(1)-优秀代码
好的代码特征可以分为外部特征和内部特征。其中,外部特征是高质量代码应有的外在表现,从结果角度衡量。对这些特征的判断无须深入代码,即使是一个不懂软件的人,也能从外部感知到。内部特征则体现了代码是否“专业”,从代码的内部质量角度衡量。经验丰富的软件工程师,只需要大致读一下代码,就能感知到代码的大致质量。优质代码的外部特征可以概括为以下5条。.实现了期望的功能。(实现了需求要求的功能)。.缺陷尽量少。 (问题单尽量少).易于理解。 (交付的代码同事之间容易看懂).易于演进。.易于复用。前两条和代码的外部质量有
2025-06-20 08:41:18
979
原创 linux驱动开发(17)-DMA(一)
直接内存访问DMA(Direct Memory Access)用来在与之间直接进行数据交换,因为这个过程无须CPU的干预,所以对于与系统有大量数据交换的设备而言,如果能充分利用DMA特性,可以大大提高系统性能。这种情况对设备驱动程序提出了新的要求,即必须能很好地支持设备的DMA操作。我们将首先讨论Linux内核中的DMA层,然后再讨论DMA操作的核心即DMA内存映射,包括一致性DMA映射、流式DMA映射和分散/聚集映射。
2025-06-19 08:53:51
1238
原创 linux驱动开发(15)-内存映射mmap(三)
vmalloc_32分配一段连续的虚拟地址,然后通过内核的伙伴系统分配相应的物理页面并提交到前面的虚拟地址上。现在我们有了设备内存,它保存在cam->frame_buffer中,接下来的核心是在while循环中,它首先通过kvirt_to_pa(pos)得到设备内存所映射到的物理内存地址,然后通过remap_pfn_range将用户进程空间的虚拟地址映射到该物理内存页面上,这里每次调用remap_pfn_range映射一个物理页面,直到所有的设备内存页面全部被映射完毕。现在显卡设备多以PCI设备形式存在,
2025-06-19 08:53:30
721
原创 linux驱动开发(16)-内存映射mmap(四)
这里再给出一个将内核空间某一物理页面映射到用户空间的例子,这里的物理页面其实可引申为设备的内存(比如某PCI-E显卡设备的Frame Buffer所在的这个例子将展示用户空间如何通过mmap来映射某一段物理地址空间并对其进行操作。内核空间的物理页面通过alloc_pages获得,其对应的将用printk打印出来,这样用户空间才可以告诉mmap函数要映射到哪个物理页面上。为了使代码简洁,程序去除了对错误情况的处理。内核模块使用了定时器每隔10秒钟打印被用户空间映射的。
2025-06-18 09:08:36
643
原创 linux驱动开发(14)-内存映射mmap(二)
现在继续回过头来看看do_mmap_pgoff函数的后续部分。实际的映射工作在mmap_region函数中完成(显然需要设备驱动程序中实现的mmap方法的配合)。当该函数被调用时,参数addr已经指向了一块空闲的待映射的MMAP区域中的起始地址,所以函数会首先利用kmem_cache_zalloc分配出一个struct vm_area_struct实例对象,然后对其初始化。
2025-06-18 09:08:13
1139
原创 linux驱动开发(13)-内存映射mmap(一)
内存映射与内存分配不同,它要完成的任务是将映射到用户空间或者直接使用用户空间中的地址,设备程序这样做的目的显然是从提升系统性能的角度出发。如果将这种概念更具体化,内存映射部分实际上是描述如何实现设备驱动程序中file_operations中的mmap方法。
2025-06-17 09:28:47
844
原创 linux驱动开发(12)-原子操作和队列
系统中的Task A和B运行之后,g_flag会是多少,2吗?答案是有可能!这是一个典型的对变量的非原子操作可能导致错误结果的例子。如果Task A先被调度运行,在其执行完L1尚未执行L2时,Task B开始被调度执行,在其执行完整个代码后g_flag=1,然后系统又调度Task A从L2处继续执行,因为此前已执行了L1,导致EAX=0,经L2后,EAX=1,于是在L3执行完后,g_flag=1。
2025-06-17 09:28:27
1005
原创 linux驱动开发(11)-其它互斥机制
顺序锁的设计思想是,对某一共享数据读取时不加锁,写的时候加锁。为了保证读取的过程中不会因为写入者的出现导致该共享数据的更新,需要在读取者和写入者之间引入一整型变量,称为顺序值sequence。读取者在开始读取前读取该sequence,在读取动作完成后再重读该值,如果与之前读取到的值不一致,则说明本次读取操作过程中发生了数据更新,读取操作无效。因此要求写入者在开始写入的时候要更新sequence的值。Linux内核中seqlock定义如下:无符号型整数sequence用来协调读取者与写入者的操作,spinl
2025-06-16 08:44:29
584
原创 linux驱动开发(10)- 互斥锁mutex
endif };#endif如同struct semaphore一样,对struct mutex的初始化不能直接通过操作其成员变量的方式进行,而应该利用内核提供的宏或者函数。1structmutex如果在程序执行期间要初始化一个mutex变量,则可以使用mutex_init宏。
2025-06-16 08:44:05
843
原创 linux驱动开发(9)- 信号量
其中,lock是个自旋锁变量,用于实现对信号量的另一个成员count的原子操作。无符号整型变量count用于表示通过该信号量允许进入临界区的执行路径的个数。wait_list用于管理所有在该信号量上睡眠的进程,无法获得该信号量的进程将进入睡眠状态。如果驱动程序中定义了一个struct semaphore型的信号量变量,需要注意的是不要直接对该变量的成员进行赋值,而应该使用sema_init函数来初始化该信号量。
2025-06-14 09:55:42
1121
原创 linux驱动开发(8)- spin_lock变体
与spin_lock_irq类似的还有一个spin_lock_irqsave宏,它与spin_lock_irq函数最大的区别是,在关闭中断前会将处理器当前的FLAGS寄存器的值保存在一个变量中,当调用对应的spin_unlock_irqrestore来释放锁时,会将spin_lock_irqsave中保存的FLAGS值重新写回到寄存器中。在raw_spin_unlock_irq函数中除了调用do_raw_spin_unlock做实际的解锁操作外,还会打开本地处理器上的中断,以及开启内核的可抢占性。
2025-06-14 09:53:30
1122
原创 linux驱动开发(7)-互斥与同步
Linux内核为设备驱动程序等内核模块提供的互斥与同步的内核机制。如果运行的系统中自始至终只有一个执行路径,那么无须考虑互斥与同步的问题,然而不幸的是,现代的Linux系统不只支持多进程而且支持多处理器,在这样的环境下,当多个执行路径并发执行时确保对共享资源的访问安全是驱动程序员不得不面对的问题。概括地说,互斥是指对资源的排他性访问,而同步则要对进程执行的先后顺序做出妥善的安排。因为程序的并发执行而导致的竞态是Linux内核中一个非常复杂的方面。
2025-06-12 22:55:28
1100
原创 linux驱动开发(6)-内核虚拟空间管理
此时就可以使用per-CPU变量,让系统中每个处理器都使用独属于自己的该变量的副本,这样在变量更新时就无须考虑多处理器的锁定问题,可以提高性能。实际代码中ioremap还有一些相关的变体,包括ioremap_nocache、ioremap_cached等,这些变体的主要功能是通过加入一些映射标志位来影响相关内核页表项的设置,比如设备驱动程序中最常用的ioremap_nocache,就是通过清除页表项中的C(ache)标志[插图],使得处理器在访问这段地址时不会被cache,这对外设空间的地址是非常重要的。
2025-06-12 22:54:47
1193
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人