You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'driver-core-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core merge from Greg Kroah-Hartman: "Here is the big driver core update for 3.7-rc1. A number of firmware_class.c updates (as you saw a month or so ago), and some hyper-v updates and some printk fixes as well. All patches that are outside of the drivers/base area have been acked by the respective maintainers, and have all been in the linux-next tree for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>" * tag 'driver-core-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (95 commits) memory: tegra{20,30}-mc: Fix reading incorrect register in mc_readl() device.h: Add missing inline to #ifndef CONFIG_PRINTK dev_vprintk_emit memory: emif: Add ifdef CONFIG_DEBUG_FS guard for emif_debugfs_[init|exit] Documentation: Fixes some translation error in Documentation/zh_CN/gpio.txt Documentation: Remove 3 byte redundant code at the head of the Documentation/zh_CN/arm/booting Documentation: Chinese translation of Documentation/video4linux/omap3isp.txt device and dynamic_debug: Use dev_vprintk_emit and dev_printk_emit dev: Add dev_vprintk_emit and dev_printk_emit netdev_printk/netif_printk: Remove a superfluous logging colon netdev_printk/dynamic_netdev_dbg: Directly call printk_emit dev_dbg/dynamic_debug: Update to use printk_emit, optimize stack driver-core: Shut up dev_dbg_reatelimited() without DEBUG tools/hv: Parse /etc/os-release tools/hv: Check for read/write errors tools/hv: Fix exit() error code tools/hv: Fix file handle leak Tools: hv: Implement the KVP verb - KVP_OP_GET_IP_INFO Tools: hv: Rename the function kvp_get_ip_address() Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO Tools: hv: Add an example script to configure an interface ...
This commit is contained in:
@@ -13,7 +13,7 @@ Description:
|
||||
accessory cables have such capability. For example,
|
||||
the 30-pin port of Nuri board (/arch/arm/mach-exynos)
|
||||
may have both HDMI and Charger attached, or analog audio,
|
||||
video, and USB cables attached simulteneously.
|
||||
video, and USB cables attached simultaneously.
|
||||
|
||||
If there are cables mutually exclusive with each other,
|
||||
such binary relations may be expressed with extcon_dev's
|
||||
@@ -35,7 +35,7 @@ Description:
|
||||
The /sys/class/extcon/.../state shows and stores the cable
|
||||
attach/detach information of the corresponding extcon object.
|
||||
If the extcon object has an optional callback "show_state"
|
||||
defined, the showing function is overriden with the optional
|
||||
defined, the showing function is overridden with the optional
|
||||
callback.
|
||||
|
||||
If the default callback for showing function is used, the
|
||||
@@ -46,19 +46,19 @@ Description:
|
||||
TA=1
|
||||
EAR_JACK=0
|
||||
#
|
||||
In this example, the extcon device have USB_OTG and TA
|
||||
In this example, the extcon device has USB_OTG and TA
|
||||
cables attached and HDMI and EAR_JACK cables detached.
|
||||
|
||||
In order to update the state of an extcon device, enter a hex
|
||||
state number starting with 0x.
|
||||
echo 0xHEX > state
|
||||
state number starting with 0x:
|
||||
# echo 0xHEX > state
|
||||
|
||||
This updates the whole state of the extcon dev.
|
||||
This updates the whole state of the extcon device.
|
||||
Inputs of all the methods are required to meet the
|
||||
mutually_exclusive contidions if they exist.
|
||||
mutually_exclusive conditions if they exist.
|
||||
|
||||
It is recommended to use this "global" state interface if
|
||||
you need to enter the value atomically. The later state
|
||||
you need to set the value atomically. The later state
|
||||
interface associated with each cable cannot update
|
||||
multiple cable states of an extcon device simultaneously.
|
||||
|
||||
@@ -73,7 +73,7 @@ What: /sys/class/extcon/.../cable.x/state
|
||||
Date: February 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/extcon/.../cable.x/name shows and stores the
|
||||
The /sys/class/extcon/.../cable.x/state shows and stores the
|
||||
state of cable "x" (integer between 0 and 31) of an extcon
|
||||
device. The state value is either 0 (detached) or 1
|
||||
(attached).
|
||||
@@ -83,8 +83,8 @@ Date: December 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
Shows the relations of mutually exclusiveness. For example,
|
||||
if the mutually_exclusive array of extcon_dev is
|
||||
{0x3, 0x5, 0xC, 0x0}, the, the output is:
|
||||
if the mutually_exclusive array of extcon device is
|
||||
{0x3, 0x5, 0xC, 0x0}, then the output is:
|
||||
# ls mutually_exclusive/
|
||||
0x3
|
||||
0x5
|
||||
|
||||
@@ -15,8 +15,8 @@ Debugfs is typically mounted with a command like:
|
||||
mount -t debugfs none /sys/kernel/debug
|
||||
|
||||
(Or an equivalent /etc/fstab line).
|
||||
The debugfs root directory is accessible by anyone by default. To
|
||||
restrict access to the tree the "uid", "gid" and "mode" mount
|
||||
The debugfs root directory is accessible only to the root user by
|
||||
default. To change access to the tree the "uid", "gid" and "mode" mount
|
||||
options can be used.
|
||||
|
||||
Note that the debugfs API is exported GPL-only to modules.
|
||||
|
||||
@@ -284,9 +284,11 @@ instead, it is associated with the ktype. So let us introduce struct
|
||||
kobj_type:
|
||||
|
||||
struct kobj_type {
|
||||
void (*release)(struct kobject *);
|
||||
void (*release)(struct kobject *kobj);
|
||||
const struct sysfs_ops *sysfs_ops;
|
||||
struct attribute **default_attrs;
|
||||
struct attribute **default_attrs;
|
||||
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
|
||||
const void *(*namespace)(struct kobject *kobj);
|
||||
};
|
||||
|
||||
This structure is used to describe a particular type of kobject (or, more
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
Chinese translated version of Documentation/arm/Booting
|
||||
|
||||
If you have any comment or update to the content, please contact the
|
||||
original document maintainer directly. However, if you have a problem
|
||||
communicating in English you can also ask the Chinese maintainer for
|
||||
help. Contact the Chinese maintainer if this translation is outdated
|
||||
or if there is a problem with the translation.
|
||||
|
||||
Maintainer: Russell King <linux@arm.linux.org.uk>
|
||||
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/arm/Booting 的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
|
||||
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
|
||||
译存在问题,请联系中文版维护者。
|
||||
|
||||
英文版维护者: Russell King <linux@arm.linux.org.uk>
|
||||
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
|
||||
启动 ARM Linux
|
||||
==============
|
||||
|
||||
作者:Russell King
|
||||
日期:2002年5月18日
|
||||
|
||||
以下文档适用于 2.4.18-rmk6 及以上版本。
|
||||
|
||||
为了启动 ARM Linux,你需要一个引导装载程序(boot loader),
|
||||
它是一个在主内核启动前运行的一个小程序。引导装载程序需要初始化各种
|
||||
设备,并最终调用 Linux 内核,将信息传递给内核。
|
||||
|
||||
从本质上讲,引导装载程序应提供(至少)以下功能:
|
||||
|
||||
1、设置和初始化 RAM。
|
||||
2、初始化一个串口。
|
||||
3、检测机器的类型(machine type)。
|
||||
4、设置内核标签列表(tagged list)。
|
||||
5、调用内核映像。
|
||||
|
||||
|
||||
1、设置和初始化 RAM
|
||||
-------------------
|
||||
|
||||
现有的引导加载程序: 强制
|
||||
新开发的引导加载程序: 强制
|
||||
|
||||
引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。
|
||||
这个操作的执行是设备依赖的。(它可能使用内部算法来自动定位和计算所有
|
||||
RAM,或可能使用对这个设备已知的 RAM 信息,还可能使用任何引导装载程序
|
||||
设计者想到的匹配方法。)
|
||||
|
||||
|
||||
2、初始化一个串口
|
||||
-----------------------------
|
||||
|
||||
现有的引导加载程序: 可选、建议
|
||||
新开发的引导加载程序: 可选、建议
|
||||
|
||||
引导加载程序应该初始化并使能一个目标板上的串口。这允许内核串口驱动
|
||||
自动检测哪个串口用于内核控制台。(一般用于调试或与目标板通信。)
|
||||
|
||||
作为替代方案,引导加载程序也可以通过标签列表传递相关的'console='
|
||||
选项给内核以指定某个串口,而串口数据格式的选项在以下文档中描述:
|
||||
|
||||
Documentation/kernel-parameters.txt。
|
||||
|
||||
|
||||
3、检测机器类型
|
||||
--------------------------
|
||||
|
||||
现有的引导加载程序: 可选
|
||||
新开发的引导加载程序: 强制
|
||||
|
||||
引导加载程序应该通过某些方式检测自身所处的机器类型。这是一个硬件
|
||||
代码或通过查看所连接的硬件用某些算法得到,这些超出了本文档的范围。
|
||||
引导加载程序最终必须能提供一个 MACH_TYPE_xxx 值给内核。
|
||||
(详见 linux/arch/arm/tools/mach-types )。
|
||||
|
||||
4、设置启动数据
|
||||
------------------
|
||||
|
||||
现有的引导加载程序: 可选、强烈建议
|
||||
新开发的引导加载程序: 强制
|
||||
|
||||
引导加载程序必须提供标签列表或者 dtb 映像以传递配置数据给内核。启动
|
||||
数据的物理地址通过寄存器 r2 传递给内核。
|
||||
|
||||
4a、设置内核标签列表
|
||||
--------------------------------
|
||||
|
||||
bootloader 必须创建和初始化内核标签列表。一个有效的标签列表以
|
||||
ATAG_CORE 标签开始,并以 ATAG_NONE 标签结束。ATAG_CORE 标签可以是
|
||||
空的,也可以是非空。一个空 ATAG_CORE 标签其 size 域设置为
|
||||
‘2’(0x00000002)。ATAG_NONE 标签的 size 域必须设置为零。
|
||||
|
||||
在列表中可以保存任意数量的标签。对于一个重复的标签是追加到之前标签
|
||||
所携带的信息之后,还是会覆盖原来的信息,是未定义的。某些标签的行为
|
||||
是前者,其他是后者。
|
||||
|
||||
bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
|
||||
因此,最小的标签列表如下所示:
|
||||
|
||||
+-----------+
|
||||
基地址 -> | ATAG_CORE | |
|
||||
+-----------+ |
|
||||
| ATAG_MEM | | 地址增长方向
|
||||
+-----------+ |
|
||||
| ATAG_NONE | |
|
||||
+-----------+ v
|
||||
|
||||
标签列表应该保存在系统的 RAM 中。
|
||||
|
||||
标签列表必须置于内核自解压和 initrd'bootp' 程序都不会覆盖的内存区。
|
||||
建议放在 RAM 的头 16KiB 中。
|
||||
|
||||
4b、设置设备树
|
||||
-------------------------
|
||||
|
||||
bootloader 必须以 64bit 地址对齐的形式加载一个设备树映像(dtb)到系统
|
||||
RAM 中,并用启动数据初始化它。dtb 格式在文档
|
||||
Documentation/devicetree/booting-without-of.txt 中。内核将会在
|
||||
dtb 物理地址处查找 dtb 魔数值(0xd00dfeed),以确定 dtb 是否已经代替
|
||||
标签列表被传递进来。
|
||||
|
||||
bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
|
||||
dtb 必须置于内核自解压不会覆盖的内存区。建议将其放置于 RAM 的头 16KiB
|
||||
中。但是不可将其放置于“0”物理地址处,因为内核认为:r2 中为 0,意味着
|
||||
没有标签列表和 dtb 传递过来。
|
||||
|
||||
5、调用内核映像
|
||||
---------------------------
|
||||
|
||||
现有的引导加载程序: 强制
|
||||
新开发的引导加载程序: 强制
|
||||
|
||||
调用内核映像 zImage 有两个选择。如果 zImge 保存在 flash 中,且是为了
|
||||
在 flash 中直接运行而被正确链接的。这样引导加载程序就可以在 flash 中
|
||||
直接调用 zImage。
|
||||
|
||||
zImage 也可以被放在系统 RAM(任意位置)中被调用。注意:内核使用映像
|
||||
基地址的前 16KB RAM 空间来保存页表。建议将映像置于 RAM 的 32KB 处。
|
||||
|
||||
对于以上任意一种情况,都必须符合以下启动状态:
|
||||
|
||||
- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而被破坏。
|
||||
这可能可以节省你许多的调试时间。
|
||||
|
||||
- CPU 寄存器配置
|
||||
r0 = 0,
|
||||
r1 = (在上面 3 中获取的)机器类型码。
|
||||
r2 = 标签列表在系统 RAM 中的物理地址,或
|
||||
设备树块(dtb)在系统 RAM 中的物理地址
|
||||
|
||||
- CPU 模式
|
||||
所有形式的中断必须被禁止 (IRQs 和 FIQs)
|
||||
CPU 必须处于 SVC 模式。(对于 Angel 调试有特例存在)
|
||||
|
||||
- 缓存,MMUs
|
||||
MMU 必须关闭。
|
||||
指令缓存开启或关闭都可以。
|
||||
数据缓存必须关闭。
|
||||
|
||||
- 引导加载程序应该通过直接跳转到内核映像的第一条指令来调用内核映像。
|
||||
|
||||
对于支持 ARM 指令集的 CPU,跳入内核入口时必须处在 ARM 状态,即使
|
||||
对于 Thumb-2 内核也是如此。
|
||||
|
||||
对于仅支持 Thumb 指令集的 CPU,比如 Cortex-M 系列的 CPU,跳入
|
||||
内核入口时必须处于 Thumb 状态。
|
||||
@@ -0,0 +1,71 @@
|
||||
Chinese translated version of Documentation/basic_profiling
|
||||
|
||||
If you have any comment or update to the content, please post to LKML directly.
|
||||
However, if you have problem communicating in English you can also ask the
|
||||
Chinese maintainer for help. Contact the Chinese maintainer, if this
|
||||
translation is outdated or there is problem with translation.
|
||||
|
||||
Chinese maintainer: Liang Xie <xieliang@xiaomi.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/basic_profiling的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
|
||||
以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
|
||||
|
||||
中文版维护者: 谢良 Liang Xie <xieliang007@gmail.com>
|
||||
中文版翻译者: 谢良 Liang Xie <xieliang007@gmail.com>
|
||||
中文版校译者:
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
|
||||
下面这些说明指令都是非常基础的,如果你想进一步了解请阅读相关专业文档:)
|
||||
请不要再在本文档增加新的内容,但可以修复文档中的错误:)(mbligh@aracnet.com)
|
||||
感谢John Levon,Dave Hansen等在撰写时的帮助
|
||||
|
||||
<test> 用于表示要测量的目标
|
||||
请先确保您已经有正确的System.map / vmlinux配置!
|
||||
|
||||
对于linux系统来说,配置vmlinuz最容易的方法可能就是使用“make install”,然后修改
|
||||
/sbin/installkernel将vmlinux拷贝到/boot目录,而System.map通常是默认安装好的
|
||||
|
||||
Readprofile
|
||||
-----------
|
||||
2.6系列内核需要版本相对较新的readprofile,比如util-linux 2.12a中包含的,可以从:
|
||||
|
||||
http://www.kernel.org/pub/linux/utils/util-linux/ 下载
|
||||
|
||||
大部分linux发行版已经包含了.
|
||||
|
||||
启用readprofile需要在kernel启动命令行增加”profile=2“
|
||||
|
||||
clear readprofile -r
|
||||
<test>
|
||||
dump output readprofile -m /boot/System.map > captured_profile
|
||||
|
||||
Oprofile
|
||||
--------
|
||||
|
||||
从http://oprofile.sourceforge.net/获取源代码(请参考Changes以获取匹配的版本)
|
||||
在kernel启动命令行增加“idle=poll”
|
||||
|
||||
配置CONFIG_PROFILING=y和CONFIG_OPROFILE=y然后重启进入新kernel
|
||||
|
||||
./configure --with-kernel-support
|
||||
make install
|
||||
|
||||
想得到好的测量结果,请确保启用了本地APIC特性。如果opreport显示有0Hz CPU,
|
||||
说明APIC特性没有开启。另外注意idle=poll选项可能有损性能。
|
||||
|
||||
One time setup:
|
||||
opcontrol --setup --vmlinux=/boot/vmlinux
|
||||
|
||||
clear opcontrol --reset
|
||||
start opcontrol --start
|
||||
<test>
|
||||
stop opcontrol --stop
|
||||
dump output opreport > output_file
|
||||
|
||||
如果只看kernel相关的报告结果,请运行命令 opreport -l /boot/vmlinux > output_file
|
||||
|
||||
通过reset选项可以清理过期统计数据,相当于重启的效果。
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
Chinese translated version of Documentation/filesystems/sysfs.txt
|
||||
|
||||
If you have any comment or update to the content, please contact the
|
||||
original document maintainer directly. However, if you have a problem
|
||||
communicating in English you can also ask the Chinese maintainer for
|
||||
help. Contact the Chinese maintainer if this translation is outdated
|
||||
or if there is a problem with the translation.
|
||||
|
||||
Maintainer: Patrick Mochel <mochel@osdl.org>
|
||||
Mike Murphy <mamurph@cs.clemson.edu>
|
||||
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/filesystems/sysfs.txt 的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
|
||||
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
|
||||
译存在问题,请联系中文版维护者。
|
||||
英文版维护者: Patrick Mochel <mochel@osdl.org>
|
||||
Mike Murphy <mamurph@cs.clemson.edu>
|
||||
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
sysfs - 用于导出内核对象(kobject)的文件系统
|
||||
|
||||
Patrick Mochel <mochel@osdl.org>
|
||||
Mike Murphy <mamurph@cs.clemson.edu>
|
||||
|
||||
修订: 16 August 2011
|
||||
原始版本: 10 January 2003
|
||||
|
||||
|
||||
sysfs 简介:
|
||||
~~~~~~~~~~
|
||||
|
||||
sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核
|
||||
数据结构及其属性,以及它们之间的关联到用户空间的方法。
|
||||
|
||||
sysfs 始终与 kobject 的底层结构紧密相关。请阅读
|
||||
Documentation/kobject.txt 文档以获得更多关于 kobject 接口的
|
||||
信息。
|
||||
|
||||
|
||||
使用 sysfs
|
||||
~~~~~~~~~~~
|
||||
|
||||
只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可
|
||||
通过以下命令挂载它:
|
||||
|
||||
mount -t sysfs sysfs /sys
|
||||
|
||||
|
||||
创建目录
|
||||
~~~~~~~~
|
||||
|
||||
任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个
|
||||
目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递
|
||||
内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的
|
||||
共同祖先;例如:某些对象属于某个子系统。
|
||||
|
||||
Sysfs 在与其目录关联的 sysfs_dirent 对象中内部保存一个指向实现
|
||||
目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于
|
||||
kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject
|
||||
引用计数只能通过 sysfs_schedule_callback() 函数直接修改。
|
||||
|
||||
|
||||
属性
|
||||
~~~~
|
||||
|
||||
kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义
|
||||
了面向文件 I/O 操作的方法,以提供对内核属性的读写。
|
||||
|
||||
|
||||
属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个
|
||||
文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值
|
||||
数组也被广泛地接受。
|
||||
|
||||
混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是
|
||||
很丢脸的,而且其代码会在未通知作者的情况下被重写。
|
||||
|
||||
|
||||
一个简单的属性结构定义如下:
|
||||
|
||||
struct attribute {
|
||||
char * name;
|
||||
struct module *owner;
|
||||
umode_t mode;
|
||||
};
|
||||
|
||||
|
||||
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
|
||||
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
|
||||
|
||||
|
||||
一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定
|
||||
对象类型的属性定义自己的属性结构体和封装函数。
|
||||
|
||||
例如:驱动程序模型定义的 device_attribute 结构体如下:
|
||||
|
||||
struct device_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
};
|
||||
|
||||
int device_create_file(struct device *, const struct device_attribute *);
|
||||
void device_remove_file(struct device *, const struct device_attribute *);
|
||||
|
||||
为了定义设备属性,同时定义了一下辅助宏:
|
||||
|
||||
#define DEVICE_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
例如:声明
|
||||
|
||||
static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
|
||||
|
||||
等同于如下代码:
|
||||
|
||||
static struct device_attribute dev_attr_foo = {
|
||||
.attr = {
|
||||
.name = "foo",
|
||||
.mode = S_IWUSR | S_IRUGO,
|
||||
.show = show_foo,
|
||||
.store = store_foo,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
子系统特有的回调函数
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作,
|
||||
以帮助读写调用实现属性所有者的显示和储存方法。
|
||||
|
||||
struct sysfs_ops {
|
||||
ssize_t (*show)(struct kobject *, struct attribute *, char *);
|
||||
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
|
||||
};
|
||||
|
||||
[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的
|
||||
描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的
|
||||
文档]
|
||||
|
||||
sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会
|
||||
将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后
|
||||
调用相关联的函数。
|
||||
|
||||
|
||||
示例:
|
||||
|
||||
#define to_dev(obj) container_of(obj, struct device, kobj)
|
||||
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
|
||||
|
||||
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct device_attribute *dev_attr = to_dev_attr(attr);
|
||||
struct device *dev = to_dev(kobj);
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
if (dev_attr->show)
|
||||
ret = dev_attr->show(dev, dev_attr, buf);
|
||||
if (ret >= (ssize_t)PAGE_SIZE) {
|
||||
print_symbol("dev_attr_show: %s returned bad count\n",
|
||||
(unsigned long)dev_attr->show);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
读写属性数据
|
||||
~~~~~~~~~~~~
|
||||
|
||||
在声明属性时,必须指定 show() 或 store() 方法,以实现属性的
|
||||
读或写。这些方法的类型应该和以下的设备属性定义一样简单。
|
||||
|
||||
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。
|
||||
|
||||
sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。
|
||||
Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时
|
||||
会出现以下的行为:
|
||||
|
||||
- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性
|
||||
应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将
|
||||
不会不太高。
|
||||
|
||||
这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间
|
||||
向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将
|
||||
再次被调用,以重新填充缓存。
|
||||
|
||||
- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。
|
||||
之后 Sysfs 传递整个缓冲区给 store() 方法。
|
||||
|
||||
当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要
|
||||
改变的值,然后回写整个缓冲区。
|
||||
|
||||
在读写属性值时,属性方法的执行应操作相同的缓冲区。
|
||||
|
||||
注记:
|
||||
|
||||
- 写操作导致的 show() 方法重载,会忽略当前文件位置。
|
||||
|
||||
- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。
|
||||
|
||||
- show() 方法应该返回写入缓冲区的字节数,也就是 snprintf()的
|
||||
返回值。
|
||||
|
||||
- show() 应始终使用 snprintf()。
|
||||
|
||||
- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回
|
||||
count 参数。
|
||||
|
||||
- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个
|
||||
错误值。
|
||||
|
||||
- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在
|
||||
内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要,
|
||||
应该实现一个检测机制。
|
||||
|
||||
一个简单的(未经实验证实的)设备属性实现如下:
|
||||
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
|
||||
}
|
||||
|
||||
static ssize_t store_name(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
snprintf(dev->name, sizeof(dev->name), "%.*s",
|
||||
(int)min(count, sizeof(dev->name) - 1), buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
|
||||
|
||||
|
||||
(注意:真正的实现不允许用户空间设置设备名。)
|
||||
|
||||
顶层目录布局
|
||||
~~~~~~~~~~~~
|
||||
|
||||
sysfs 目录的安排显示了内核数据结构之间的关系。
|
||||
|
||||
顶层 sysfs 目录如下:
|
||||
|
||||
block/
|
||||
bus/
|
||||
class/
|
||||
dev/
|
||||
devices/
|
||||
firmware/
|
||||
net/
|
||||
fs/
|
||||
|
||||
devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核
|
||||
设备树,反映了设备的层次结构。
|
||||
|
||||
bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个
|
||||
子目录:
|
||||
|
||||
devices/
|
||||
drivers/
|
||||
|
||||
devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的
|
||||
设备目录。
|
||||
|
||||
drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里
|
||||
假定驱动没有跨越多个总线类型)。
|
||||
|
||||
fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须
|
||||
在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.txt)。
|
||||
|
||||
dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以
|
||||
<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录
|
||||
中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找
|
||||
设备 sysfs 接口快捷的方法。
|
||||
|
||||
更多有关 driver-model 的特性信息可以在 Documentation/driver-model/
|
||||
中找到。
|
||||
|
||||
|
||||
TODO: 完成这一节。
|
||||
|
||||
|
||||
当前接口
|
||||
~~~~~~~~
|
||||
|
||||
以下的接口层普遍存在于当前的sysfs中:
|
||||
|
||||
- 设备 (include/linux/device.h)
|
||||
----------------------------------
|
||||
结构体:
|
||||
|
||||
struct device_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
};
|
||||
|
||||
声明:
|
||||
|
||||
DEVICE_ATTR(_name, _mode, _show, _store);
|
||||
|
||||
增/删属性:
|
||||
|
||||
int device_create_file(struct device *dev, const struct device_attribute * attr);
|
||||
void device_remove_file(struct device *dev, const struct device_attribute * attr);
|
||||
|
||||
|
||||
- 总线驱动程序 (include/linux/device.h)
|
||||
--------------------------------------
|
||||
结构体:
|
||||
|
||||
struct bus_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct bus_type *, char * buf);
|
||||
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
|
||||
};
|
||||
|
||||
声明:
|
||||
|
||||
BUS_ATTR(_name, _mode, _show, _store)
|
||||
|
||||
增/删属性:
|
||||
|
||||
int bus_create_file(struct bus_type *, struct bus_attribute *);
|
||||
void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
||||
|
||||
|
||||
- 设备驱动程序 (include/linux/device.h)
|
||||
-----------------------------------------
|
||||
|
||||
结构体:
|
||||
|
||||
struct driver_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device_driver *, char * buf);
|
||||
ssize_t (*store)(struct device_driver *, const char * buf,
|
||||
size_t count);
|
||||
};
|
||||
|
||||
声明:
|
||||
|
||||
DRIVER_ATTR(_name, _mode, _show, _store)
|
||||
|
||||
增/删属性:
|
||||
|
||||
int driver_create_file(struct device_driver *, const struct driver_attribute *);
|
||||
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
|
||||
|
||||
|
||||
文档
|
||||
~~~~
|
||||
|
||||
sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。
|
||||
对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs
|
||||
属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
Chinese translated version of Documentation/video4linux/omap3isp.txt
|
||||
|
||||
If you have any comment or update to the content, please contact the
|
||||
original document maintainer directly. However, if you have a problem
|
||||
communicating in English you can also ask the Chinese maintainer for
|
||||
help. Contact the Chinese maintainer if this translation is outdated
|
||||
or if there is a problem with the translation.
|
||||
|
||||
Maintainer: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
Sakari Ailus <sakari.ailus@iki.fi>
|
||||
David Cohen <dacohen@gmail.com>
|
||||
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/video4linux/omap3isp.txt 的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
|
||||
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
|
||||
译存在问题,请联系中文版维护者。
|
||||
英文版维护者: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
Sakari Ailus <sakari.ailus@iki.fi>
|
||||
David Cohen <dacohen@gmail.com>
|
||||
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
|
||||
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
OMAP 3 图像信号处理器 (ISP) 驱动
|
||||
|
||||
Copyright (C) 2010 Nokia Corporation
|
||||
Copyright (C) 2009 Texas Instruments, Inc.
|
||||
|
||||
联系人: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
Sakari Ailus <sakari.ailus@iki.fi>
|
||||
David Cohen <dacohen@gmail.com>
|
||||
|
||||
|
||||
介绍
|
||||
===
|
||||
|
||||
本文档介绍了由 drivers/media/video/omap3isp 加载的德州仪器
|
||||
(TI)OMAP 3 图像信号处理器 (ISP) 驱动。原始驱动由德州仪器(TI)
|
||||
编写,但此后由诺基亚重写了两次。
|
||||
|
||||
驱动已在以下 OMAP 3 系列的芯片中成功使用:
|
||||
|
||||
3430
|
||||
3530
|
||||
3630
|
||||
|
||||
驱动实现了 V4L2、媒体控制器和 v4l2_subdev 接口。支持内核中使用
|
||||
v4l2_subdev 接口的传感器、镜头和闪光灯驱动。
|
||||
|
||||
|
||||
拆分为子设备
|
||||
==========
|
||||
|
||||
OMAP 3 ISP 被拆分为 V4L2 子设备,ISP中的每个模块都由一个子设备
|
||||
来表示。每个子设备向用户空间提供一个 V4L2 子设备接口。
|
||||
|
||||
OMAP3 ISP CCP2
|
||||
OMAP3 ISP CSI2a
|
||||
OMAP3 ISP CCDC
|
||||
OMAP3 ISP preview
|
||||
OMAP3 ISP resizer
|
||||
OMAP3 ISP AEWB
|
||||
OMAP3 ISP AF
|
||||
OMAP3 ISP histogram
|
||||
|
||||
ISP 中每个可能的连接都通过一个链接嵌入到媒体控制器接口中。详见例程 [2]。
|
||||
|
||||
|
||||
控制 OMAP 3 ISP
|
||||
==============
|
||||
|
||||
通常,对 OMAP 3 ISP 的配置会在下一帧起始时生效。在传感器垂直消隐期间,
|
||||
模块变为空闲时完成配置。在内存到内存的操作中,视频管道一次处理一帧。
|
||||
应用配置应在帧间完成。
|
||||
|
||||
ISP 中的所有模块,除 CSI-2 和 (可能存在的)CCP2 接收器外,都必须
|
||||
接收完整的帧数据。因此,传感器必须保证从不发送部分帧数据给ISP。
|
||||
|
||||
Autoidle(自动空闲)功能至少在 3430 的 ISP 模块中确实存在一些问题。
|
||||
当 omap3isp 模块参数 autoidle 非零时,autoidle(自动空闲)功能
|
||||
仅在 3630 中启用了。
|
||||
|
||||
|
||||
事件机制
|
||||
======
|
||||
|
||||
OMAP 3 ISP 驱动在 CCDC 和统计(AEWB、AF 和 直方图)子设备中支持
|
||||
V4L2 事件机制接口。
|
||||
|
||||
CCDC 子设备通过 HS_VS 中断,处理 V4L2_EVENT_FRAME_SYNC 类型
|
||||
事件,用于告知帧起始。早期版本的驱动则使用 V4L2_EVENT_OMAP3ISP_HS_VS。
|
||||
当在 CCDC 模块中接收到起始帧的第一行时,会准确地触发事件。这个事件
|
||||
可以在 CCDC 子设备中“订阅”。
|
||||
|
||||
(当使用并行接口时,必须注意正确地配置 VS 信号极性。而当使用串行接收时
|
||||
这个会自动校正。)
|
||||
|
||||
每个统计子设备都可以产生事件。每当一个统计缓冲区可由用户空间应用程序
|
||||
通过 VIDIOC_OMAP3ISP_STAT_REQ IOCTL 操作获取时,就会产生一个
|
||||
事件。当前存在以下事件:
|
||||
|
||||
V4L2_EVENT_OMAP3ISP_AEWB
|
||||
V4L2_EVENT_OMAP3ISP_AF
|
||||
V4L2_EVENT_OMAP3ISP_HIST
|
||||
|
||||
这些 ioctl 的事件数据类型为 struct omap3isp_stat_event_status
|
||||
结构体。如果出现计算错误的统计,也同样会产生一个事件,但没有相关的统计
|
||||
数据缓冲区。这种情况下 omap3isp_stat_event_status.buf_err 会被
|
||||
设置为非零值。
|
||||
|
||||
|
||||
私有 IOCTL
|
||||
==========
|
||||
|
||||
OMAP 3 ISP 驱动支持标准的 V4L2 IOCTL 以及可能存在且实用的控制。但
|
||||
ISP 提供的许多功能都不在标准 IOCTL 之列,例如 gamma(伽马)表和统计
|
||||
数据采集配置等。
|
||||
|
||||
通常,会有一个私有 ioctl 用于配置每个包含硬件依赖功能的模块。
|
||||
|
||||
支持以下私有 IOCTL:
|
||||
|
||||
VIDIOC_OMAP3ISP_CCDC_CFG
|
||||
VIDIOC_OMAP3ISP_PRV_CFG
|
||||
VIDIOC_OMAP3ISP_AEWB_CFG
|
||||
VIDIOC_OMAP3ISP_HIST_CFG
|
||||
VIDIOC_OMAP3ISP_AF_CFG
|
||||
VIDIOC_OMAP3ISP_STAT_REQ
|
||||
VIDIOC_OMAP3ISP_STAT_EN
|
||||
|
||||
在 include/linux/omap3isp.h 中描述了这些 ioctl 使用的参数结构体。
|
||||
与特定 ISP 模块相关的 ISP 自身的详细功能在技术参考手册 (TRMs)中有
|
||||
描述,详见文档结尾。
|
||||
|
||||
虽然在不使用任何私有 IOCTL 的情况下使用 ISP 驱动是可能的,但这样无法
|
||||
获得最佳的图像质量。AEWB、AF 和 直方图(译者注:一般用于自动曝光和增益
|
||||
控制,以及图像均衡等)模块无法在未使用适当的私有 IOCTL 配置的情况下使用。
|
||||
|
||||
|
||||
CCDC 和 preview(预览)模块 IOCTL
|
||||
===============================
|
||||
|
||||
VIDIOC_OMAP3ISP_CCDC_CFG 和 VIDIOC_OMAP3ISP_PRV_CFG IOCTL
|
||||
被分别用于配置、启用和禁用 CCDC 和 preview(预览)模块的功能。在它们
|
||||
所控制的模块中,两个 IOCTL 控制多种功能。VIDIOC_OMAP3ISP_CCDC_CFG IOCTL
|
||||
接受一个指向 omap3isp_ccdc_update_config 结构体的指针作为它的参数。
|
||||
同样的,VIDIOC_OMAP3ISP_PRV_CFG 接受一个指向 omap3isp_prev_update_config
|
||||
结构体的指针。以上两个结构体定义位于 [1]。
|
||||
|
||||
这些结构体中的 update 域标识是否针对指定的功能更新配置,而 flag 域
|
||||
则标识是启用还是禁用此功能。
|
||||
|
||||
update 和 flag 位接受以下掩码值。CCDC 和 preview(预览)模块的
|
||||
每个单独功能都与一个 flag 关联(禁用或启用;在结构体中 flag 域的
|
||||
一部分)和一个指向功能配置数据的指针。
|
||||
|
||||
对于 VIDIOC_OMAP3ISP_CCDC_CFG,下面列出了 update 和 flag 域
|
||||
中的有效值。 这些值可能会在同一个 IOCTL 调用中配置多个功能。
|
||||
|
||||
OMAP3ISP_CCDC_ALAW
|
||||
OMAP3ISP_CCDC_LPF
|
||||
OMAP3ISP_CCDC_BLCLAMP
|
||||
OMAP3ISP_CCDC_BCOMP
|
||||
OMAP3ISP_CCDC_FPC
|
||||
OMAP3ISP_CCDC_CULL
|
||||
OMAP3ISP_CCDC_CONFIG_LSC
|
||||
OMAP3ISP_CCDC_TBL_LSC
|
||||
|
||||
针对 VIDIOC_OMAP3ISP_PRV_CFG 的相应值如下:
|
||||
|
||||
OMAP3ISP_PREV_LUMAENH
|
||||
OMAP3ISP_PREV_INVALAW
|
||||
OMAP3ISP_PREV_HRZ_MED
|
||||
OMAP3ISP_PREV_CFA
|
||||
OMAP3ISP_PREV_CHROMA_SUPP
|
||||
OMAP3ISP_PREV_WB
|
||||
OMAP3ISP_PREV_BLKADJ
|
||||
OMAP3ISP_PREV_RGB2RGB
|
||||
OMAP3ISP_PREV_COLOR_CONV
|
||||
OMAP3ISP_PREV_YC_LIMIT
|
||||
OMAP3ISP_PREV_DEFECT_COR
|
||||
OMAP3ISP_PREV_GAMMABYPASS
|
||||
OMAP3ISP_PREV_DRK_FRM_CAPTURE
|
||||
OMAP3ISP_PREV_DRK_FRM_SUBTRACT
|
||||
OMAP3ISP_PREV_LENS_SHADING
|
||||
OMAP3ISP_PREV_NF
|
||||
OMAP3ISP_PREV_GAMMA
|
||||
|
||||
在启用某个功能的时候,相关的配置数据指针不可为 NULL。在禁用某个功能时,
|
||||
配置数据指针会被忽略。
|
||||
|
||||
|
||||
统计模块 IOCTL
|
||||
=============
|
||||
|
||||
统计子设备相较于其他子设备提供了更多动态配置选项。在图像处理流水线处于
|
||||
工作状态时,它们可以被启用、禁用和重配。
|
||||
|
||||
统计模块总是从 CCDC 中获取输入的图像数据(由于直方图内存读取未实现)。
|
||||
统计数据可由用户通过统计子设备节点使用私有 IOCTL 获取。
|
||||
|
||||
AEWB、AF 和 直方图子设备提供的私有 IOCTL 极大程度上反应了 ISP 硬件
|
||||
提供的寄存器级接口。有些方面纯粹和驱动程序的实现相关,这些将在下面讨论。
|
||||
|
||||
VIDIOC_OMAP3ISP_STAT_EN
|
||||
-----------------------
|
||||
|
||||
这个私有 IOCTL 启用/禁用 一个统计模块。如果这个申请在视频流启动前完成,
|
||||
它将在视频流水线开始工作时生效。如果视频流水线已经处于工作状态了,它将在
|
||||
CCDC 变为空闲时生效。
|
||||
|
||||
VIDIOC_OMAP3ISP_AEWB_CFG, VIDIOC_OMAP3ISP_HIST_CFG and VIDIOC_OMAP3ISP_AF_CFG
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
这些 IOCTL 用于配置模块。它们要求用户应用程序对硬件有深入的认识。对
|
||||
大多数域的解释可以在 OMAP 的 TRM 中找到。以下两个域对于以上所有的
|
||||
私有 IOCTL 配置都很常见,由于他们没有在 TRM 中提及,故需要对其有
|
||||
更好的认识。
|
||||
|
||||
omap3isp_[h3a_af/h3a_aewb/hist]_config.buf_size:
|
||||
|
||||
模块在内部处理自身缓冲。对模块数据输出所必需的缓存大小依赖于已申请的配置。
|
||||
虽然驱动支持在视频流工作时重新配置,但对于所需缓存量大于模块启用时内部
|
||||
所分配数量的情况,则不支持重新配置。在这种情况下将返回 -EBUSY。为了避免
|
||||
此类状况,无论是禁用/重配/启用模块,还是第一次配置时申请必须的缓存大小,
|
||||
都应在模块禁用的情况下进行。
|
||||
|
||||
内部缓冲分配的大小需综合考虑所申请配置的最小缓存量以及 buf_size 域中
|
||||
所设的值。如果 buf_size 域在[minimum(最小值), maximum(最大值)]
|
||||
缓冲大小范围之外,则应该将其调整到其范围中。驱动则会选择最大值。正确的
|
||||
buf_size 值将回写到用户应用程序中。
|
||||
|
||||
omap3isp_[h3a_af/h3a_aewb/hist]_config.config_counter:
|
||||
|
||||
由于配置并未在申请之后同步生效,驱动必须提供一个跟踪这类信息的方法,
|
||||
以提供更准确的数据。在一个配置被申请之后,返回到用户空间应用程序的
|
||||
config_counter 是一个与其配置相关的唯一值。当用户应用程序接收到
|
||||
一个缓冲可用或一个新的缓冲申请事件时,这个 config_counter 用于
|
||||
一个缓冲数据和一个配置的匹配。
|
||||
|
||||
VIDIOC_OMAP3ISP_STAT_REQ
|
||||
------------------------
|
||||
|
||||
将内部缓冲队列中最早的数据发送到用户空间,然后丢弃此缓冲区。
|
||||
omap3isp_stat_data.frame_number 域与视频缓冲的 field_count
|
||||
域相匹配。
|
||||
|
||||
|
||||
技术参考手册 (TRMs) 和其他文档
|
||||
==========================
|
||||
|
||||
OMAP 3430 TRM:
|
||||
<URL:http://focus.ti.com/pdfs/wtbu/OMAP34xx_ES3.1.x_PUBLIC_TRM_vZM.zip>
|
||||
参考于 2011-03-05.
|
||||
|
||||
OMAP 35xx TRM:
|
||||
<URL:http://www.ti.com/litv/pdf/spruf98o> 参考于 2011-03-05.
|
||||
|
||||
OMAP 3630 TRM:
|
||||
<URL:http://focus.ti.com/pdfs/wtbu/OMAP36xx_ES1.x_PUBLIC_TRM_vQ.zip>
|
||||
参考于 2011-03-05.
|
||||
|
||||
DM 3730 TRM:
|
||||
<URL:http://www.ti.com/litv/pdf/sprugn4h> 参考于 2011-03-06.
|
||||
|
||||
|
||||
参考资料
|
||||
=======
|
||||
|
||||
[1] include/linux/omap3isp.h
|
||||
|
||||
[2] http://git.ideasonboard.org/?p=media-ctl.git;a=summary
|
||||
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -2865,7 +2865,9 @@ F: include/linux/firewire*.h
|
||||
F: tools/firewire/
|
||||
|
||||
FIRMWARE LOADER (request_firmware)
|
||||
S: Orphan
|
||||
M: Ming Lei <ming.lei@canonical.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/firmware_class/
|
||||
F: drivers/base/firmware*.c
|
||||
F: include/linux/firmware.h
|
||||
|
||||
@@ -412,14 +412,6 @@ config TILE_USB
|
||||
config NEED_BOUNCE_POOL
|
||||
def_bool USB_OHCI_HCD
|
||||
|
||||
config HOTPLUG
|
||||
bool "Support for hot-pluggable devices"
|
||||
---help---
|
||||
Say Y here if you want to plug devices into your computer while
|
||||
the system is running, and be able to use them quickly. In many
|
||||
cases, the devices can likewise be unplugged at any time too.
|
||||
One well-known example of this is USB.
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -172,24 +172,6 @@ config CMDLINE
|
||||
|
||||
source "mm/Kconfig"
|
||||
|
||||
config HOTPLUG
|
||||
bool "Support for hot-pluggable devices"
|
||||
help
|
||||
Say Y here if you want to plug devices into your computer while
|
||||
the system is running, and be able to use them quickly. In many
|
||||
cases, the devices can likewise be unplugged at any time too.
|
||||
|
||||
One well known example of this is PCMCIA- or PC-cards, credit-card
|
||||
size devices such as network cards, modems or hard drives which are
|
||||
plugged into slots found on all modern laptop computers. Another
|
||||
example, used on modern desktops as well as laptops, is USB.
|
||||
|
||||
Enable HOTPLUG and build a modular kernel. Get agent software
|
||||
(from <http://linux-hotplug.sourceforge.net/>) and install it.
|
||||
Then your kernel will automatically call out to a user mode "policy
|
||||
agent" (/sbin/hotplug) to load modules and set up software needed
|
||||
to use devices as you hotplug them.
|
||||
|
||||
source "drivers/pcmcia/Kconfig"
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
|
||||
+68
-37
@@ -184,6 +184,17 @@ static void device_release(struct kobject *kobj)
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct device_private *p = dev->p;
|
||||
|
||||
/*
|
||||
* Some platform devices are driven without driver attached
|
||||
* and managed resources may have been acquired. Make sure
|
||||
* all resources are released.
|
||||
*
|
||||
* Drivers still can add resources into device after device
|
||||
* is deleted but alive, so release devres here to avoid
|
||||
* possible memory leak.
|
||||
*/
|
||||
devres_release_all(dev);
|
||||
|
||||
if (dev->release)
|
||||
dev->release(dev);
|
||||
else if (dev->type && dev->type->release)
|
||||
@@ -1196,13 +1207,6 @@ void device_del(struct device *dev)
|
||||
bus_remove_device(dev);
|
||||
driver_deferred_probe_del(dev);
|
||||
|
||||
/*
|
||||
* Some platform devices are driven without driver attached
|
||||
* and managed resources may have been acquired. Make sure
|
||||
* all resources are released.
|
||||
*/
|
||||
devres_release_all(dev);
|
||||
|
||||
/* Notify the platform of the removal, in case they
|
||||
* need to do anything...
|
||||
*/
|
||||
@@ -1861,26 +1865,20 @@ void device_shutdown(void)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
int __dev_printk(const char *level, const struct device *dev,
|
||||
struct va_format *vaf)
|
||||
static int
|
||||
create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
|
||||
{
|
||||
char dict[128];
|
||||
const char *level_extra = "";
|
||||
size_t dictlen = 0;
|
||||
const char *subsys;
|
||||
|
||||
if (!dev)
|
||||
return printk("%s(NULL device *): %pV", level, vaf);
|
||||
size_t pos = 0;
|
||||
|
||||
if (dev->class)
|
||||
subsys = dev->class->name;
|
||||
else if (dev->bus)
|
||||
subsys = dev->bus->name;
|
||||
else
|
||||
goto skip;
|
||||
return 0;
|
||||
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"SUBSYSTEM=%s", subsys);
|
||||
pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
|
||||
|
||||
/*
|
||||
* Add device identifier DEVICE=:
|
||||
@@ -1896,32 +1894,63 @@ int __dev_printk(const char *level, const struct device *dev,
|
||||
c = 'b';
|
||||
else
|
||||
c = 'c';
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=%c%u:%u",
|
||||
c, MAJOR(dev->devt), MINOR(dev->devt));
|
||||
pos++;
|
||||
pos += snprintf(hdr + pos, hdrlen - pos,
|
||||
"DEVICE=%c%u:%u",
|
||||
c, MAJOR(dev->devt), MINOR(dev->devt));
|
||||
} else if (strcmp(subsys, "net") == 0) {
|
||||
struct net_device *net = to_net_dev(dev);
|
||||
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=n%u", net->ifindex);
|
||||
pos++;
|
||||
pos += snprintf(hdr + pos, hdrlen - pos,
|
||||
"DEVICE=n%u", net->ifindex);
|
||||
} else {
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=+%s:%s", subsys, dev_name(dev));
|
||||
pos++;
|
||||
pos += snprintf(hdr + pos, hdrlen - pos,
|
||||
"DEVICE=+%s:%s", subsys, dev_name(dev));
|
||||
}
|
||||
skip:
|
||||
if (level[2])
|
||||
level_extra = &level[2]; /* skip past KERN_SOH "L" */
|
||||
|
||||
return printk_emit(0, level[1] - '0',
|
||||
dictlen ? dict : NULL, dictlen,
|
||||
"%s %s: %s%pV",
|
||||
dev_driver_string(dev), dev_name(dev),
|
||||
level_extra, vaf);
|
||||
return pos;
|
||||
}
|
||||
EXPORT_SYMBOL(create_syslog_header);
|
||||
|
||||
int dev_vprintk_emit(int level, const struct device *dev,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
char hdr[128];
|
||||
size_t hdrlen;
|
||||
|
||||
hdrlen = create_syslog_header(dev, hdr, sizeof(hdr));
|
||||
|
||||
return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args);
|
||||
}
|
||||
EXPORT_SYMBOL(dev_vprintk_emit);
|
||||
|
||||
int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
r = dev_vprintk_emit(level, dev, fmt, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(dev_printk_emit);
|
||||
|
||||
static int __dev_printk(const char *level, const struct device *dev,
|
||||
struct va_format *vaf)
|
||||
{
|
||||
if (!dev)
|
||||
return printk("%s(NULL device *): %pV", level, vaf);
|
||||
|
||||
return dev_printk_emit(level[1] - '0', dev,
|
||||
"%s %s: %pV",
|
||||
dev_driver_string(dev), dev_name(dev), vaf);
|
||||
}
|
||||
EXPORT_SYMBOL(__dev_printk);
|
||||
|
||||
int dev_printk(const char *level, const struct device *dev,
|
||||
const char *fmt, ...)
|
||||
@@ -1936,6 +1965,7 @@ int dev_printk(const char *level, const struct device *dev,
|
||||
vaf.va = &args;
|
||||
|
||||
r = __dev_printk(level, dev, &vaf);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
@@ -1955,6 +1985,7 @@ int func(const struct device *dev, const char *fmt, ...) \
|
||||
vaf.va = &args; \
|
||||
\
|
||||
r = __dev_printk(kern_level, dev, &vaf); \
|
||||
\
|
||||
va_end(args); \
|
||||
\
|
||||
return r; \
|
||||
|
||||
@@ -143,6 +143,48 @@ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
|
||||
EXPORT_SYMBOL_GPL(devres_alloc);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* devres_for_each_res - Resource iterator
|
||||
* @dev: Device to iterate resource from
|
||||
* @release: Look for resources associated with this release function
|
||||
* @match: Match function (optional)
|
||||
* @match_data: Data for the match function
|
||||
* @fn: Function to be called for each matched resource.
|
||||
* @data: Data for @fn, the 3rd parameter of @fn
|
||||
*
|
||||
* Call @fn for each devres of @dev which is associated with @release
|
||||
* and for which @match returns 1.
|
||||
*
|
||||
* RETURNS:
|
||||
* void
|
||||
*/
|
||||
void devres_for_each_res(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data,
|
||||
void (*fn)(struct device *, void *, void *),
|
||||
void *data)
|
||||
{
|
||||
struct devres_node *node;
|
||||
struct devres_node *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dev->devres_lock, flags);
|
||||
list_for_each_entry_safe_reverse(node, tmp,
|
||||
&dev->devres_head, entry) {
|
||||
struct devres *dr = container_of(node, struct devres, node);
|
||||
|
||||
if (node->release != release)
|
||||
continue;
|
||||
if (match && !match(dev, dr->data, match_data))
|
||||
continue;
|
||||
fn(dev, dr->data, data);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->devres_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devres_for_each_res);
|
||||
|
||||
/**
|
||||
* devres_free - Free device resource data
|
||||
* @res: Pointer to devres data to free
|
||||
|
||||
+741
-111
File diff suppressed because it is too large
Load Diff
+38
-3
@@ -20,9 +20,13 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
/* For automatically allocated device IDs */
|
||||
static DEFINE_IDA(platform_devid_ida);
|
||||
|
||||
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
|
||||
driver))
|
||||
|
||||
@@ -99,6 +103,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
|
||||
for (i = 0; i < dev->num_resources; i++) {
|
||||
struct resource *r = &dev->resource[i];
|
||||
|
||||
if (unlikely(!r->name))
|
||||
continue;
|
||||
|
||||
if (type == resource_type(r) && !strcmp(r->name, name))
|
||||
return r;
|
||||
}
|
||||
@@ -263,7 +270,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
|
||||
*/
|
||||
int platform_device_add(struct platform_device *pdev)
|
||||
{
|
||||
int i, ret = 0;
|
||||
int i, ret;
|
||||
|
||||
if (!pdev)
|
||||
return -EINVAL;
|
||||
@@ -273,10 +280,27 @@ int platform_device_add(struct platform_device *pdev)
|
||||
|
||||
pdev->dev.bus = &platform_bus_type;
|
||||
|
||||
if (pdev->id != -1)
|
||||
switch (pdev->id) {
|
||||
default:
|
||||
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
|
||||
else
|
||||
break;
|
||||
case PLATFORM_DEVID_NONE:
|
||||
dev_set_name(&pdev->dev, "%s", pdev->name);
|
||||
break;
|
||||
case PLATFORM_DEVID_AUTO:
|
||||
/*
|
||||
* Automatically allocated device ID. We mark it as such so
|
||||
* that we remember it must be freed, and we append a suffix
|
||||
* to avoid namespace collision with explicit IDs.
|
||||
*/
|
||||
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
pdev->id = ret;
|
||||
pdev->id_auto = true;
|
||||
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdev->num_resources; i++) {
|
||||
struct resource *p, *r = &pdev->resource[i];
|
||||
@@ -309,6 +333,11 @@ int platform_device_add(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
failed:
|
||||
if (pdev->id_auto) {
|
||||
ida_simple_remove(&platform_devid_ida, pdev->id);
|
||||
pdev->id = PLATFORM_DEVID_AUTO;
|
||||
}
|
||||
|
||||
while (--i >= 0) {
|
||||
struct resource *r = &pdev->resource[i];
|
||||
unsigned long type = resource_type(r);
|
||||
@@ -317,6 +346,7 @@ int platform_device_add(struct platform_device *pdev)
|
||||
release_resource(r);
|
||||
}
|
||||
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_device_add);
|
||||
@@ -336,6 +366,11 @@ void platform_device_del(struct platform_device *pdev)
|
||||
if (pdev) {
|
||||
device_del(&pdev->dev);
|
||||
|
||||
if (pdev->id_auto) {
|
||||
ida_simple_remove(&platform_devid_ida, pdev->id);
|
||||
pdev->id = PLATFORM_DEVID_AUTO;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdev->num_resources; i++) {
|
||||
struct resource *r = &pdev->resource[i];
|
||||
unsigned long type = resource_type(r);
|
||||
|
||||
@@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
|
||||
return async_error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
|
||||
|
||||
/**
|
||||
* dpm_for_each_dev - device iterator.
|
||||
* @data: data for the callback.
|
||||
* @fn: function to be called for each device.
|
||||
*
|
||||
* Iterate over devices in dpm_list, and call @fn for each device,
|
||||
* passing it @data.
|
||||
*/
|
||||
void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
if (!fn)
|
||||
return;
|
||||
|
||||
device_pm_lock();
|
||||
list_for_each_entry(dev, &dpm_list, power.entry)
|
||||
fn(dev, data);
|
||||
device_pm_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpm_for_each_dev);
|
||||
|
||||
@@ -21,6 +21,12 @@ config EXTCON_GPIO
|
||||
Say Y here to enable GPIO based extcon support. Note that GPIO
|
||||
extcon supports single state per extcon instance.
|
||||
|
||||
config EXTCON_ADC_JACK
|
||||
tristate "ADC Jack extcon support"
|
||||
depends on IIO
|
||||
help
|
||||
Say Y here to enable extcon device driver based on ADC values.
|
||||
|
||||
config EXTCON_MAX77693
|
||||
tristate "MAX77693 EXTCON Support"
|
||||
depends on MFD_MAX77693
|
||||
@@ -41,7 +47,7 @@ config EXTCON_MAX8997
|
||||
|
||||
config EXTCON_ARIZONA
|
||||
tristate "Wolfson Arizona EXTCON support"
|
||||
depends on MFD_ARIZONA
|
||||
depends on MFD_ARIZONA && INPUT
|
||||
help
|
||||
Say Y here to enable support for external accessory detection
|
||||
with Wolfson Arizona devices. These are audio CODECs with
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
# Makefile for external connector class (extcon) devices
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EXTCON) += extcon_class.o
|
||||
obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
|
||||
obj-$(CONFIG_EXTCON) += extcon-class.o
|
||||
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
|
||||
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
|
||||
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
|
||||
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
|
||||
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* drivers/extcon/extcon-adc-jack.c
|
||||
*
|
||||
* Analog Jack extcon driver with ADC-based detection capability.
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* Modified for calling to IIO to get adc by <anish.singh@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/extcon/extcon-adc-jack.h>
|
||||
#include <linux/extcon.h>
|
||||
|
||||
/**
|
||||
* struct adc_jack_data - internal data for adc_jack device driver
|
||||
* @edev - extcon device.
|
||||
* @cable_names - list of supported cables.
|
||||
* @num_cables - size of cable_names.
|
||||
* @adc_conditions - list of adc value conditions.
|
||||
* @num_conditions - size of adc_conditions.
|
||||
* @irq - irq number of attach/detach event (0 if not exist).
|
||||
* @handling_delay - interrupt handler will schedule extcon event
|
||||
* handling at handling_delay jiffies.
|
||||
* @handler - extcon event handler called by interrupt handler.
|
||||
* @chan - iio channel being queried.
|
||||
*/
|
||||
struct adc_jack_data {
|
||||
struct extcon_dev edev;
|
||||
|
||||
const char **cable_names;
|
||||
int num_cables;
|
||||
struct adc_jack_cond *adc_conditions;
|
||||
int num_conditions;
|
||||
|
||||
int irq;
|
||||
unsigned long handling_delay; /* in jiffies */
|
||||
struct delayed_work handler;
|
||||
|
||||
struct iio_channel *chan;
|
||||
};
|
||||
|
||||
static void adc_jack_handler(struct work_struct *work)
|
||||
{
|
||||
struct adc_jack_data *data = container_of(to_delayed_work(work),
|
||||
struct adc_jack_data,
|
||||
handler);
|
||||
u32 state = 0;
|
||||
int ret, adc_val;
|
||||
int i;
|
||||
|
||||
ret = iio_read_channel_raw(data->chan, &adc_val);
|
||||
if (ret < 0) {
|
||||
dev_err(data->edev.dev, "read channel() error: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get state from adc value with adc_conditions */
|
||||
for (i = 0; i < data->num_conditions; i++) {
|
||||
struct adc_jack_cond *def = &data->adc_conditions[i];
|
||||
if (!def->state)
|
||||
break;
|
||||
if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
|
||||
state = def->state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* if no def has met, it means state = 0 (no cables attached) */
|
||||
|
||||
extcon_set_state(&data->edev, state);
|
||||
}
|
||||
|
||||
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
|
||||
{
|
||||
struct adc_jack_data *data = _data;
|
||||
|
||||
schedule_delayed_work(&data->handler, data->handling_delay);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit adc_jack_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct adc_jack_data *data;
|
||||
struct adc_jack_pdata *pdata = pdev->dev.platform_data;
|
||||
int i, err = 0;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->edev.name = pdata->name;
|
||||
|
||||
if (!pdata->cable_names) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: cable_names not defined.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->edev.supported_cable = pdata->cable_names;
|
||||
|
||||
/* Check the length of array and set num_cables */
|
||||
for (i = 0; data->edev.supported_cable[i]; i++)
|
||||
;
|
||||
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
|
||||
i - 1);
|
||||
goto out;
|
||||
}
|
||||
data->num_cables = i;
|
||||
|
||||
if (!pdata->adc_conditions ||
|
||||
!pdata->adc_conditions[0].state) {
|
||||
err = -EINVAL;
|
||||
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
|
||||
goto out;
|
||||
}
|
||||
data->adc_conditions = pdata->adc_conditions;
|
||||
|
||||
/* Check the length of array and set num_conditions */
|
||||
for (i = 0; data->adc_conditions[i].state; i++)
|
||||
;
|
||||
data->num_conditions = i;
|
||||
|
||||
data->chan = iio_channel_get(dev_name(&pdev->dev),
|
||||
pdata->consumer_channel);
|
||||
if (IS_ERR(data->chan)) {
|
||||
err = PTR_ERR(data->chan);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
|
||||
|
||||
INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
err = extcon_dev_register(&data->edev, &pdev->dev);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (!data->irq) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed\n");
|
||||
err = -ENODEV;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
err = request_any_context_irq(data->irq, adc_jack_irq_thread,
|
||||
pdata->irq_flags, pdata->name, data);
|
||||
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
|
||||
err = -EINVAL;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
err_irq:
|
||||
extcon_dev_unregister(&data->edev);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit adc_jack_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct adc_jack_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(data->irq, data);
|
||||
cancel_work_sync(&data->handler.work);
|
||||
extcon_dev_unregister(&data->edev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver adc_jack_driver = {
|
||||
.probe = adc_jack_probe,
|
||||
.remove = __devexit_p(adc_jack_remove),
|
||||
.driver = {
|
||||
.name = "adc-jack",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(adc_jack_driver);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user