mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
Merge branch 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
* 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (50 commits) printk: do not mangle valid userspace syslog prefixes efivars: Add Documentation efivars: Expose efivars functionality to external drivers. efivars: Parameterize operations. efivars: Split out variable registration efivars: parameterize efivars efivars: Make efivars bin_attributes dynamic efivars: move efivars globals into struct efivars drivers:misc: ti-st: fix debugging code kref: Fix typo in kref documentation UIO: add PRUSS UIO driver support Fix spelling mistakes in Documentation/zh_CN/SubmittingPatches firmware: Fix unaligned memory accesses in dmi-sysfs firmware: Add documentation for /sys/firmware/dmi firmware: Expose DMI type 15 System Event Log firmware: Break out system_event_log in dmi-sysfs firmware: Basic dmi-sysfs support firmware: Add DMI entry types to the headers Driver core: convert platform_{get,set}_drvdata to static inline functions Translate linux-2.6/Documentation/magic-number.txt into Chinese ...
This commit is contained in:
commit
a5e6b135bd
75
Documentation/ABI/stable/sysfs-firmware-efi-vars
Normal file
75
Documentation/ABI/stable/sysfs-firmware-efi-vars
Normal file
@ -0,0 +1,75 @@
|
||||
What: /sys/firmware/efi/vars
|
||||
Date: April 2004
|
||||
Contact: Matt Domsch <Matt_Domsch@dell.com>
|
||||
Description:
|
||||
This directory exposes interfaces for interactive with
|
||||
EFI variables. For more information on EFI variables,
|
||||
see 'Variable Services' in the UEFI specification
|
||||
(section 7.2 in specification version 2.3 Errata D).
|
||||
|
||||
In summary, EFI variables are named, and are classified
|
||||
into separate namespaces through the use of a vendor
|
||||
GUID. They also have an arbitrary binary value
|
||||
associated with them.
|
||||
|
||||
The efivars module enumerates these variables and
|
||||
creates a separate directory for each one found. Each
|
||||
directory has a name of the form "<key>-<vendor guid>"
|
||||
and contains the following files:
|
||||
|
||||
attributes: A read-only text file enumerating the
|
||||
EFI variable flags. Potential values
|
||||
include:
|
||||
|
||||
EFI_VARIABLE_NON_VOLATILE
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
||||
EFI_VARIABLE_RUNTIME_ACCESS
|
||||
EFI_VARIABLE_HARDWARE_ERROR_RECORD
|
||||
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
|
||||
|
||||
See the EFI documentation for an
|
||||
explanation of each of these variables.
|
||||
|
||||
data: A read-only binary file that can be read
|
||||
to attain the value of the EFI variable
|
||||
|
||||
guid: The vendor GUID of the variable. This
|
||||
should always match the GUID in the
|
||||
variable's name.
|
||||
|
||||
raw_var: A binary file that can be read to obtain
|
||||
a structure that contains everything
|
||||
there is to know about the variable.
|
||||
For structure definition see "struct
|
||||
efi_variable" in the kernel sources.
|
||||
|
||||
This file can also be written to in
|
||||
order to update the value of a variable.
|
||||
For this to work however, all fields of
|
||||
the "struct efi_variable" passed must
|
||||
match byte for byte with the structure
|
||||
read out of the file, save for the value
|
||||
portion.
|
||||
|
||||
**Note** the efi_variable structure
|
||||
read/written with this file contains a
|
||||
'long' type that may change widths
|
||||
depending on your underlying
|
||||
architecture.
|
||||
|
||||
size: As ASCII representation of the size of
|
||||
the variable's value.
|
||||
|
||||
|
||||
In addition, two other magic binary files are provided
|
||||
in the top-level directory and are used for adding and
|
||||
removing variables:
|
||||
|
||||
new_var: Takes a "struct efi_variable" and
|
||||
instructs the EFI firmware to create a
|
||||
new variable.
|
||||
|
||||
del_var: Takes a "struct efi_variable" and
|
||||
instructs the EFI firmware to remove any
|
||||
variable that has a matching vendor GUID
|
||||
and variable key name.
|
110
Documentation/ABI/testing/sysfs-firmware-dmi
Normal file
110
Documentation/ABI/testing/sysfs-firmware-dmi
Normal file
@ -0,0 +1,110 @@
|
||||
What: /sys/firmware/dmi/
|
||||
Date: February 2011
|
||||
Contact: Mike Waychison <mikew@google.com>
|
||||
Description:
|
||||
Many machines' firmware (x86 and ia64) export DMI /
|
||||
SMBIOS tables to the operating system. Getting at this
|
||||
information is often valuable to userland, especially in
|
||||
cases where there are OEM extensions used.
|
||||
|
||||
The kernel itself does not rely on the majority of the
|
||||
information in these tables being correct. It equally
|
||||
cannot ensure that the data as exported to userland is
|
||||
without error either.
|
||||
|
||||
DMI is structured as a large table of entries, where
|
||||
each entry has a common header indicating the type and
|
||||
length of the entry, as well as 'handle' that is
|
||||
supposed to be unique amongst all entries.
|
||||
|
||||
Some entries are required by the specification, but many
|
||||
others are optional. In general though, users should
|
||||
never expect to find a specific entry type on their
|
||||
system unless they know for certain what their firmware
|
||||
is doing. Machine to machine will vary.
|
||||
|
||||
Multiple entries of the same type are allowed. In order
|
||||
to handle these duplicate entry types, each entry is
|
||||
assigned by the operating system an 'instance', which is
|
||||
derived from an entry type's ordinal position. That is
|
||||
to say, if there are 'N' multiple entries with the same type
|
||||
'T' in the DMI tables (adjacent or spread apart, it
|
||||
doesn't matter), they will be represented in sysfs as
|
||||
entries "T-0" through "T-(N-1)":
|
||||
|
||||
Example entry directories:
|
||||
|
||||
/sys/firmware/dmi/entries/17-0
|
||||
/sys/firmware/dmi/entries/17-1
|
||||
/sys/firmware/dmi/entries/17-2
|
||||
/sys/firmware/dmi/entries/17-3
|
||||
...
|
||||
|
||||
Instance numbers are used in lieu of the firmware
|
||||
assigned entry handles as the kernel itself makes no
|
||||
guarantees that handles as exported are unique, and
|
||||
there are likely firmware images that get this wrong in
|
||||
the wild.
|
||||
|
||||
Each DMI entry in sysfs has the common header values
|
||||
exported as attributes:
|
||||
|
||||
handle : The 16bit 'handle' that is assigned to this
|
||||
entry by the firmware. This handle may be
|
||||
referred to by other entries.
|
||||
length : The length of the entry, as presented in the
|
||||
entry itself. Note that this is _not the
|
||||
total count of bytes associated with the
|
||||
entry_. This value represents the length of
|
||||
the "formatted" portion of the entry. This
|
||||
"formatted" region is sometimes followed by
|
||||
the "unformatted" region composed of nul
|
||||
terminated strings, with termination signalled
|
||||
by a two nul characters in series.
|
||||
raw : The raw bytes of the entry. This includes the
|
||||
"formatted" portion of the entry, the
|
||||
"unformatted" strings portion of the entry,
|
||||
and the two terminating nul characters.
|
||||
type : The type of the entry. This value is the same
|
||||
as found in the directory name. It indicates
|
||||
how the rest of the entry should be
|
||||
interpreted.
|
||||
instance: The instance ordinal of the entry for the
|
||||
given type. This value is the same as found
|
||||
in the parent directory name.
|
||||
position: The position of the entry within the entirety
|
||||
of the entirety.
|
||||
|
||||
=== Entry Specialization ===
|
||||
|
||||
Some entry types may have other information available in
|
||||
sysfs.
|
||||
|
||||
--- Type 15 - System Event Log ---
|
||||
|
||||
This entry allows the firmware to export a log of
|
||||
events the system has taken. This information is
|
||||
typically backed by nvram, but the implementation
|
||||
details are abstracted by this table. This entries data
|
||||
is exported in the directory:
|
||||
|
||||
/sys/firmware/dmi/entries/15-0/system_event_log
|
||||
|
||||
and has the following attributes (documented in the
|
||||
SMBIOS / DMI specification under "System Event Log (Type 15)":
|
||||
|
||||
area_length
|
||||
header_start_offset
|
||||
data_start_offset
|
||||
access_method
|
||||
status
|
||||
change_token
|
||||
access_method_address
|
||||
header_format
|
||||
per_log_type_descriptor_length
|
||||
type_descriptors_supported_count
|
||||
|
||||
As well, the kernel exports the binary attribute:
|
||||
|
||||
raw_event_log : The raw binary bits of the event log
|
||||
as described by the DMI entry.
|
48
Documentation/ABI/testing/sysfs-platform-kim
Normal file
48
Documentation/ABI/testing/sysfs-platform-kim
Normal file
@ -0,0 +1,48 @@
|
||||
What: /sys/devices/platform/kim/dev_name
|
||||
Date: January 2010
|
||||
KernelVersion: 2.6.38
|
||||
Contact: "Pavan Savoy" <pavan_savoy@ti.com>
|
||||
Description:
|
||||
Name of the UART device at which the WL128x chip
|
||||
is connected. example: "/dev/ttyS0".
|
||||
The device name flows down to architecture specific board
|
||||
initialization file from the SFI/ATAGS bootloader
|
||||
firmware. The name exposed is read from the user-space
|
||||
dameon and opens the device when install is requested.
|
||||
|
||||
What: /sys/devices/platform/kim/baud_rate
|
||||
Date: January 2010
|
||||
KernelVersion: 2.6.38
|
||||
Contact: "Pavan Savoy" <pavan_savoy@ti.com>
|
||||
Description:
|
||||
The maximum reliable baud-rate the host can support.
|
||||
Different platforms tend to have different high-speed
|
||||
UART configurations, so the baud-rate needs to be set
|
||||
locally and also sent across to the WL128x via a HCI-VS
|
||||
command. The entry is read and made use by the user-space
|
||||
daemon when the ldisc install is requested.
|
||||
|
||||
What: /sys/devices/platform/kim/flow_cntrl
|
||||
Date: January 2010
|
||||
KernelVersion: 2.6.38
|
||||
Contact: "Pavan Savoy" <pavan_savoy@ti.com>
|
||||
Description:
|
||||
The WL128x makes use of flow control mechanism, and this
|
||||
entry most often should be 1, the host's UART is required
|
||||
to have the capability of flow-control, or else this
|
||||
entry can be made use of for exceptions.
|
||||
|
||||
What: /sys/devices/platform/kim/install
|
||||
Date: January 2010
|
||||
KernelVersion: 2.6.38
|
||||
Contact: "Pavan Savoy" <pavan_savoy@ti.com>
|
||||
Description:
|
||||
When one of the protocols Bluetooth, FM or GPS wants to make
|
||||
use of the shared UART transport, it registers to the shared
|
||||
transport driver, which will signal the user-space for opening,
|
||||
configuring baud and install line discipline via this sysfs
|
||||
entry. This entry would be polled upon by the user-space
|
||||
daemon managing the UART, and is notified about the change
|
||||
by the sysfs_notify. The value would be '1' when UART needs
|
||||
to be opened/ldisc installed, and would be '0' when UART
|
||||
is no more required and needs to be closed.
|
@ -205,12 +205,20 @@ of the characters:
|
||||
|
||||
The flags are:
|
||||
|
||||
f
|
||||
Include the function name in the printed message
|
||||
l
|
||||
Include line number in the printed message
|
||||
m
|
||||
Include module name in the printed message
|
||||
p
|
||||
Causes a printk() message to be emitted to dmesg
|
||||
t
|
||||
Include thread ID in messages not generated from interrupt context
|
||||
|
||||
Note the regexp ^[-+=][scp]+$ matches a flags specification.
|
||||
Note the regexp ^[-+=][flmpt]+$ matches a flags specification.
|
||||
Note also that there is no convenient syntax to remove all
|
||||
the flags at once, you need to use "-psc".
|
||||
the flags at once, you need to use "-flmpt".
|
||||
|
||||
|
||||
Debug messages during boot process
|
||||
|
@ -39,10 +39,12 @@ userspace. Top-level directories in sysfs represent the common
|
||||
ancestors of object hierarchies; i.e. the subsystems the objects
|
||||
belong to.
|
||||
|
||||
Sysfs internally stores the kobject that owns the directory in the
|
||||
->d_fsdata pointer of the directory's dentry. This allows sysfs to do
|
||||
reference counting directly on the kobject when the file is opened and
|
||||
closed.
|
||||
Sysfs internally stores a pointer to the kobject that implements a
|
||||
directory in the sysfs_dirent object associated with the directory. In
|
||||
the past this kobject pointer has been used by sysfs to do reference
|
||||
counting directly on the kobject whenever the file is opened or closed.
|
||||
With the current sysfs implementation the kobject reference count is
|
||||
only modified directly by the function sysfs_schedule_callback().
|
||||
|
||||
|
||||
Attributes
|
||||
@ -208,9 +210,9 @@ Other notes:
|
||||
is 4096.
|
||||
|
||||
- show() methods should return the number of bytes printed into the
|
||||
buffer. This is the return value of snprintf().
|
||||
buffer. This is the return value of scnprintf().
|
||||
|
||||
- show() should always use snprintf().
|
||||
- show() should always use scnprintf().
|
||||
|
||||
- store() should return the number of bytes used from the buffer. If the
|
||||
entire buffer has been used, just return the count argument.
|
||||
@ -229,7 +231,7 @@ A very simple (and naive) implementation of a device attribute is:
|
||||
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", dev->name);
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
|
||||
}
|
||||
|
||||
static ssize_t store_name(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -156,7 +156,7 @@ static struct my_data *get_entry()
|
||||
struct my_data *entry = NULL;
|
||||
mutex_lock(&mutex);
|
||||
if (!list_empty(&q)) {
|
||||
entry = container_of(q.next, struct my_q_entry, link);
|
||||
entry = container_of(q.next, struct my_data, link);
|
||||
kref_get(&entry->refcount);
|
||||
}
|
||||
mutex_unlock(&mutex);
|
||||
|
@ -126,36 +126,51 @@ config options.
|
||||
--------------------------------
|
||||
4 sysfs files for memory hotplug
|
||||
--------------------------------
|
||||
All sections have their device information under /sys/devices/system/memory as
|
||||
All sections have their device information in sysfs. Each section is part of
|
||||
a memory block under /sys/devices/system/memory as
|
||||
|
||||
/sys/devices/system/memory/memoryXXX
|
||||
(XXX is section id.)
|
||||
(XXX is the section id.)
|
||||
|
||||
Now, XXX is defined as start_address_of_section / section_size.
|
||||
Now, XXX is defined as (start_address_of_section / section_size) of the first
|
||||
section contained in the memory block. The files 'phys_index' and
|
||||
'end_phys_index' under each directory report the beginning and end section id's
|
||||
for the memory block covered by the sysfs directory. It is expected that all
|
||||
memory sections in this range are present and no memory holes exist in the
|
||||
range. Currently there is no way to determine if there is a memory hole, but
|
||||
the existence of one should not affect the hotplug capabilities of the memory
|
||||
block.
|
||||
|
||||
For example, assume 1GiB section size. A device for a memory starting at
|
||||
0x100000000 is /sys/device/system/memory/memory4
|
||||
(0x100000000 / 1Gib = 4)
|
||||
This device covers address range [0x100000000 ... 0x140000000)
|
||||
|
||||
Under each section, you can see 4 files.
|
||||
Under each section, you can see 4 or 5 files, the end_phys_index file being
|
||||
a recent addition and not present on older kernels.
|
||||
|
||||
/sys/devices/system/memory/memoryXXX/phys_index
|
||||
/sys/devices/system/memory/memoryXXX/start_phys_index
|
||||
/sys/devices/system/memory/memoryXXX/end_phys_index
|
||||
/sys/devices/system/memory/memoryXXX/phys_device
|
||||
/sys/devices/system/memory/memoryXXX/state
|
||||
/sys/devices/system/memory/memoryXXX/removable
|
||||
|
||||
'phys_index' : read-only and contains section id, same as XXX.
|
||||
'state' : read-write
|
||||
at read: contains online/offline state of memory.
|
||||
at write: user can specify "online", "offline" command
|
||||
'phys_device': read-only: designed to show the name of physical memory device.
|
||||
This is not well implemented now.
|
||||
'removable' : read-only: contains an integer value indicating
|
||||
whether the memory section is removable or not
|
||||
removable. A value of 1 indicates that the memory
|
||||
section is removable and a value of 0 indicates that
|
||||
it is not removable.
|
||||
'phys_index' : read-only and contains section id of the first section
|
||||
in the memory block, same as XXX.
|
||||
'end_phys_index' : read-only and contains section id of the last section
|
||||
in the memory block.
|
||||
'state' : read-write
|
||||
at read: contains online/offline state of memory.
|
||||
at write: user can specify "online", "offline" command
|
||||
which will be performed on al sections in the block.
|
||||
'phys_device' : read-only: designed to show the name of physical memory
|
||||
device. This is not well implemented now.
|
||||
'removable' : read-only: contains an integer value indicating
|
||||
whether the memory block is removable or not
|
||||
removable. A value of 1 indicates that the memory
|
||||
block is removable and a value of 0 indicates that
|
||||
it is not removable. A memory block is removable only if
|
||||
every section in the block is removable.
|
||||
|
||||
NOTE:
|
||||
These directories/files appear after physical memory hotplug phase.
|
||||
|
50
Documentation/zh_CN/SecurityBugs
Normal file
50
Documentation/zh_CN/SecurityBugs
Normal file
@ -0,0 +1,50 @@
|
||||
Chinese translated version of Documentation/SecurityBugs
|
||||
|
||||
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.
|
||||
|
||||
Chinese maintainer: Harry Wei <harryxiyou@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/SecurityBugs 的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
|
||||
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
|
||||
译存在问题,请联系中文版维护者。
|
||||
|
||||
中文版维护者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
中文版翻译者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
中文版校译者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
Linux内核开发者认为安全非常重要。因此,我们想要知道当一个有关于
|
||||
安全的漏洞被发现的时候,并且它可能会被尽快的修复或者公开。请把这个安全
|
||||
漏洞报告给Linux内核安全团队。
|
||||
|
||||
1) 联系
|
||||
|
||||
linux内核安全团队可以通过email<security@kernel.org>来联系。这是
|
||||
一组独立的安全工作人员,可以帮助改善漏洞报告并且公布和取消一个修复。安
|
||||
全团队有可能会从部分的维护者那里引进额外的帮助来了解并且修复安全漏洞。
|
||||
当遇到任何漏洞,所能提供的信息越多就越能诊断和修复。如果你不清楚什么
|
||||
是有帮助的信息,那就请重温一下REPORTING-BUGS文件中的概述过程。任
|
||||
何攻击性的代码都是非常有用的,未经报告者的同意不会被取消,除非它已经
|
||||
被公布于众。
|
||||
|
||||
2) 公开
|
||||
|
||||
Linux内核安全团队的宗旨就是和漏洞提交者一起处理漏洞的解决方案直
|
||||
到公开。我们喜欢尽快地完全公开漏洞。当一个漏洞或者修复还没有被完全地理
|
||||
解,解决方案没有通过测试或者供应商协调,可以合理地延迟公开。然而,我们
|
||||
期望这些延迟尽可能的短些,是可数的几天,而不是几个星期或者几个月。公开
|
||||
日期是通过安全团队和漏洞提供者以及供应商洽谈后的结果。公开时间表是从很
|
||||
短(特殊的,它已经被公众所知道)到几个星期。作为一个基本的默认政策,我
|
||||
们所期望通知公众的日期是7天的安排。
|
||||
|
||||
3) 保密协议
|
||||
|
||||
Linux内核安全团队不是一个正式的团体,因此不能加入任何的保密协议。
|
109
Documentation/zh_CN/SubmitChecklist
Normal file
109
Documentation/zh_CN/SubmitChecklist
Normal file
@ -0,0 +1,109 @@
|
||||
Chinese translated version of Documentation/SubmitChecklist
|
||||
|
||||
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.
|
||||
|
||||
Chinese maintainer: Harry Wei <harryxiyou@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/SubmitChecklist 的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
|
||||
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
|
||||
译存在问题,请联系中文版维护者。
|
||||
|
||||
中文版维护者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
中文版翻译者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
中文版校译者: 贾威威 Harry Wei <harryxiyou@gmail.com>
|
||||
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
Linux内核提交清单
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
这里有一些内核开发者应该做的基本事情,如果他们想看到自己的内核补丁提交
|
||||
被接受的更快。
|
||||
|
||||
这些都是超出Documentation/SubmittingPatches文档里所提供的以及其他
|
||||
关于提交Linux内核补丁的说明。
|
||||
|
||||
1:如果你使用了一个功能那么就#include定义/声明那个功能的那个文件。
|
||||
不要依靠其他间接引入定义/声明那个功能的头文件。
|
||||
|
||||
2:构建简洁适用或者更改CONFIG选项 =y,=m,或者=n。
|
||||
不要有编译警告/错误, 不要有链接警告/错误。
|
||||
|
||||
2b:通过 allnoconfig, allmodconfig
|
||||
|
||||
2c:当使用 0=builddir 成功地构建
|
||||
|
||||
3:通过使用本地交叉编译工具或者其他一些构建产所,在多CPU框架上构建。
|
||||
|
||||
4:ppc64 是一个很好的检查交叉编译的框架,因为它往往把‘unsigned long’
|
||||
当64位值来使用。
|
||||
|
||||
5:按照Documentation/CodingStyle文件里的详细描述,检查你补丁的整体风格。
|
||||
使用补丁风格检查琐碎的违规(scripts/checkpatch.pl),审核员优先提交。
|
||||
你应该调整遗留在你补丁中的所有违规。
|
||||
|
||||
6:任何更新或者改动CONFIG选项都不能打乱配置菜单。
|
||||
|
||||
7:所有的Kconfig选项更新都要有说明文字。
|
||||
|
||||
8:已经认真地总结了相关的Kconfig组合。这是很难通过测试做好的--脑力在这里下降。
|
||||
|
||||
9:检查具有简洁性。
|
||||
|
||||
10:使用'make checkstack'和'make namespacecheck'检查,然后修改所找到的问题。
|
||||
注意:堆栈检查不会明确地出现问题,但是任何的一个函数在堆栈上使用多于512字节
|
||||
都要准备修改。
|
||||
|
||||
11:包含kernel-doc到全局内核APIs文件。(不要求静态的函数,但是包含也无所谓。)
|
||||
使用'make htmldocs'或者'make mandocs'来检查kernel-doc,然后修改任何
|
||||
发现的问题。
|
||||
|
||||
12:已经通过CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT,
|
||||
CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
|
||||
CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_SPINLOCK_SLEEP测试,并且同时都
|
||||
使能。
|
||||
|
||||
13:已经都构建并且使用或者不使用 CONFIG_SMP 和 CONFIG_PREEMPT测试执行时间。
|
||||
|
||||
14:如果补丁影响IO/Disk,等等:已经通过使用或者不使用 CONFIG_LBDAF 测试。
|
||||
|
||||
15:所有的codepaths已经行使所有lockdep启用功能。
|
||||
|
||||
16:所有的/proc记录更新都要作成文件放在Documentation/目录下。
|
||||
|
||||
17:所有的内核启动参数更新都被记录到Documentation/kernel-parameters.txt文件中。
|
||||
|
||||
18:所有的模块参数更新都用MODULE_PARM_DESC()记录。
|
||||
|
||||
19:所有的用户空间接口更新都被记录到Documentation/ABI/。查看Documentation/ABI/README
|
||||
可以获得更多的信息。改变用户空间接口的补丁应该被邮件抄送给linux-api@vger.kernel.org。
|
||||
|
||||
20:检查它是不是都通过`make headers_check'。
|
||||
|
||||
21:已经通过至少引入slab和page-allocation失败检查。查看Documentation/fault-injection/。
|
||||
|
||||
22:新加入的源码已经通过`gcc -W'(使用"make EXTRA_CFLAGS=-W")编译。这样将产生很多烦恼,
|
||||
但是对于寻找漏洞很有益处,例如:"warning: comparison between signed and unsigned"。
|
||||
|
||||
23:当它被合并到-mm补丁集后再测试,用来确定它是否还和补丁队列中的其他补丁一起工作以及在VM,VFS
|
||||
和其他子系统中各个变化。
|
||||
|
||||
24:所有的内存屏障{e.g., barrier(), rmb(), wmb()}需要在源代码中的一个注释来解释他们都是干什么的
|
||||
以及原因。
|
||||
|
||||
25:如果有任何输入输出控制的补丁被添加,也要更新Documentation/ioctl/ioctl-number.txt。
|
||||
|
||||
26:如果你的更改代码依靠或者使用任何的内核APIs或者与下面的kconfig符号有关系的功能,你就要
|
||||
使用相关的kconfig符号关闭, and/or =m(如果选项提供)[在同一时间不是所用的都启用,仅仅各个或者自由
|
||||
组合他们]:
|
||||
|
||||
CONFIG_SMP, CONFIG_SYSFS, CONFIG_PROC_FS, CONFIG_INPUT, CONFIG_PCI,
|
||||
CONFIG_BLOCK, CONFIG_PM, CONFIG_HOTPLUG, CONFIG_MAGIC_SYSRQ,
|
||||
CONFIG_NET, CONFIG_INET=n (后一个使用 CONFIG_NET=y)
|
@ -100,7 +100,7 @@ http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz
|
||||
|
||||
将改动拆分,逻辑类似的放到同一个补丁文件里。
|
||||
|
||||
例如,如果你的改动里同时有bug修正和性能优化,那么把这些改动才分到两个或
|
||||
例如,如果你的改动里同时有bug修正和性能优化,那么把这些改动拆分到两个或
|
||||
者更多的补丁文件中。如果你的改动包含对API的修改,并且修改了驱动程序来适
|
||||
应这些新的API,那么把这些修改分成两个补丁。
|
||||
|
||||
@ -230,7 +230,7 @@ pref("mailnews.display.disable_format_flowed_support", true);
|
||||
些原因,修正错误,重新提交更新后的改动,是你自己的工作。
|
||||
|
||||
Linus不给出任何评论就“丢弃”你的补丁是常见的事情。在系统中这样的事情很
|
||||
平常。如果他没有接受你的补丁,也许是由于以下原本:
|
||||
平常。如果他没有接受你的补丁,也许是由于以下原因:
|
||||
* 你的补丁不能在最新版本的内核上干净的打上。
|
||||
* 你的补丁在 linux-kernel 邮件列表中没有得到充分的讨论。
|
||||
* 风格问题(参照第2小节)
|
||||
|
167
Documentation/zh_CN/magic-number.txt
Normal file
167
Documentation/zh_CN/magic-number.txt
Normal file
@ -0,0 +1,167 @@
|
||||
Chinese translated version of Documentation/magic-number.txt
|
||||
|
||||
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: Jia Wei Wei <harryxiyou@gmail.com>
|
||||
---------------------------------------------------------------------
|
||||
Documentation/magic-number.txt的中文翻译
|
||||
|
||||
如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
|
||||
以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
|
||||
|
||||
中文版维护者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com>
|
||||
中文版翻译者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com>
|
||||
中文版校译者: 贾威威 Jia Wei Wei <harryxiyou@gmail.com>
|
||||
|
||||
以下为正文
|
||||
---------------------------------------------------------------------
|
||||
这个文件是有关当前使用的魔术值注册表。当你给一个结构添加了一个魔术值,你也应该把这个魔术值添加到这个文件,因为我们最好把用于各种结构的魔术值统一起来。
|
||||
|
||||
使用魔术值来保护内核数据结构是一个非常好的主意。这就允许你在运行期检查(a)一个结构是否已经被攻击,或者(b)你已经给一个例行程序通过了一个错误的结构。后一种情况特别地有用---特别是当你通过一个空指针指向结构体的时候。tty源码,例如,经常通过特定驱动使用这种方法并且反复地排列特定方面的结构。
|
||||
|
||||
使用魔术值的方法是在结构的开始处声明的,如下:
|
||||
|
||||
struct tty_ldisc {
|
||||
int magic;
|
||||
...
|
||||
};
|
||||
|
||||
当你以后给内核添加增强功能的时候,请遵守这条规则!这样就会节省数不清的调试时间,特别是一些古怪的情况,例如,数组超出范围并且重新写了超出部分。遵守这个规则,这些情况可以被快速地,安全地避免。
|
||||
|
||||
Theodore Ts'o
|
||||
31 Mar 94
|
||||
|
||||
给当前的Linux 2.1.55添加魔术表。
|
||||
|
||||
Michael Chastain
|
||||
<mailto:mec@shout.net>
|
||||
22 Sep 1997
|
||||
|
||||
现在应该最新的Linux 2.1.112.因为在特性冻结期间,不能在2.2.x前改变任何东西。这些条目被数域所排序。
|
||||
|
||||
Krzysztof G.Baranowski
|
||||
<mailto: kgb@knm.org.pl>
|
||||
29 Jul 1998
|
||||
|
||||
更新魔术表到Linux 2.5.45。刚好越过特性冻结,但是有可能还会有一些新的魔术值在2.6.x之前融入到内核中。
|
||||
|
||||
Petr Baudis
|
||||
<pasky@ucw.cz>
|
||||
03 Nov 2002
|
||||
|
||||
更新魔术表到Linux 2.5.74。
|
||||
|
||||
Fabian Frederick
|
||||
<ffrederick@users.sourceforge.net>
|
||||
09 Jul 2003
|
||||
|
||||
魔术名 地址 结构 所在文件
|
||||
===========================================================================
|
||||
PG_MAGIC 'P' pg_{read,write}_hdr include/linux/pg.h
|
||||
CMAGIC 0x0111 user include/linux/a.out.h
|
||||
MKISS_DRIVER_MAGIC 0x04bf mkiss_channel drivers/net/mkiss.h
|
||||
RISCOM8_MAGIC 0x0907 riscom_port drivers/char/riscom8.h
|
||||
SPECIALIX_MAGIC 0x0907 specialix_port drivers/char/specialix_io8.h
|
||||
HDLC_MAGIC 0x239e n_hdlc drivers/char/n_hdlc.c
|
||||
APM_BIOS_MAGIC 0x4101 apm_user arch/i386/kernel/apm.c
|
||||
CYCLADES_MAGIC 0x4359 cyclades_port include/linux/cyclades.h
|
||||
DB_MAGIC 0x4442 fc_info drivers/net/iph5526_novram.c
|
||||
DL_MAGIC 0x444d fc_info drivers/net/iph5526_novram.c
|
||||
FASYNC_MAGIC 0x4601 fasync_struct include/linux/fs.h
|
||||
FF_MAGIC 0x4646 fc_info drivers/net/iph5526_novram.c
|
||||
ISICOM_MAGIC 0x4d54 isi_port include/linux/isicom.h
|
||||
PTY_MAGIC 0x5001 drivers/char/pty.c
|
||||
PPP_MAGIC 0x5002 ppp include/linux/if_pppvar.h
|
||||
SERIAL_MAGIC 0x5301 async_struct include/linux/serial.h
|
||||
SSTATE_MAGIC 0x5302 serial_state include/linux/serial.h
|
||||
SLIP_MAGIC 0x5302 slip drivers/net/slip.h
|
||||
STRIP_MAGIC 0x5303 strip drivers/net/strip.c
|
||||
X25_ASY_MAGIC 0x5303 x25_asy drivers/net/x25_asy.h
|
||||
SIXPACK_MAGIC 0x5304 sixpack drivers/net/hamradio/6pack.h
|
||||
AX25_MAGIC 0x5316 ax_disp drivers/net/mkiss.h
|
||||
ESP_MAGIC 0x53ee esp_struct drivers/char/esp.h
|
||||
TTY_MAGIC 0x5401 tty_struct include/linux/tty.h
|
||||
MGSL_MAGIC 0x5401 mgsl_info drivers/char/synclink.c
|
||||
TTY_DRIVER_MAGIC 0x5402 tty_driver include/linux/tty_driver.h
|
||||
MGSLPC_MAGIC 0x5402 mgslpc_info drivers/char/pcmcia/synclink_cs.c
|
||||
TTY_LDISC_MAGIC 0x5403 tty_ldisc include/linux/tty_ldisc.h
|
||||
USB_SERIAL_MAGIC 0x6702 usb_serial drivers/usb/serial/usb-serial.h
|
||||
FULL_DUPLEX_MAGIC 0x6969 drivers/net/tulip/de2104x.c
|
||||
USB_BLUETOOTH_MAGIC 0x6d02 usb_bluetooth drivers/usb/class/bluetty.c
|
||||
RFCOMM_TTY_MAGIC 0x6d02 net/bluetooth/rfcomm/tty.c
|
||||
USB_SERIAL_PORT_MAGIC 0x7301 usb_serial_port drivers/usb/serial/usb-serial.h
|
||||
CG_MAGIC 0x00090255 ufs_cylinder_group include/linux/ufs_fs.h
|
||||
A2232_MAGIC 0x000a2232 gs_port drivers/char/ser_a2232.h
|
||||
RPORT_MAGIC 0x00525001 r_port drivers/char/rocket_int.h
|
||||
LSEMAGIC 0x05091998 lse drivers/fc4/fc.c
|
||||
GDTIOCTL_MAGIC 0x06030f07 gdth_iowr_str drivers/scsi/gdth_ioctl.h
|
||||
RIEBL_MAGIC 0x09051990 drivers/net/atarilance.c
|
||||
RIO_MAGIC 0x12345678 gs_port drivers/char/rio/rio_linux.c
|
||||
SX_MAGIC 0x12345678 gs_port drivers/char/sx.h
|
||||
NBD_REQUEST_MAGIC 0x12560953 nbd_request include/linux/nbd.h
|
||||
RED_MAGIC2 0x170fc2a5 (any) mm/slab.c
|
||||
BAYCOM_MAGIC 0x19730510 baycom_state drivers/net/baycom_epp.c
|
||||
ISDN_X25IFACE_MAGIC 0x1e75a2b9 isdn_x25iface_proto_data
|
||||
drivers/isdn/isdn_x25iface.h
|
||||
ECP_MAGIC 0x21504345 cdkecpsig include/linux/cdk.h
|
||||
LSOMAGIC 0x27091997 lso drivers/fc4/fc.c
|
||||
LSMAGIC 0x2a3b4d2a ls drivers/fc4/fc.c
|
||||
WANPIPE_MAGIC 0x414C4453 sdla_{dump,exec} include/linux/wanpipe.h
|
||||
CS_CARD_MAGIC 0x43525553 cs_card sound/oss/cs46xx.c
|
||||
LABELCL_MAGIC 0x4857434c labelcl_info_s include/asm/ia64/sn/labelcl.h
|
||||
ISDN_ASYNC_MAGIC 0x49344C01 modem_info include/linux/isdn.h
|
||||
CTC_ASYNC_MAGIC 0x49344C01 ctc_tty_info drivers/s390/net/ctctty.c
|
||||
ISDN_NET_MAGIC 0x49344C02 isdn_net_local_s drivers/isdn/i4l/isdn_net_lib.h
|
||||
SAVEKMSG_MAGIC2 0x4B4D5347 savekmsg arch/*/amiga/config.c
|
||||
STLI_BOARDMAGIC 0x4bc6c825 stlibrd include/linux/istallion.h
|
||||
CS_STATE_MAGIC 0x4c4f4749 cs_state sound/oss/cs46xx.c
|
||||
SLAB_C_MAGIC 0x4f17a36d kmem_cache mm/slab.c
|
||||
COW_MAGIC 0x4f4f4f4d cow_header_v1 arch/um/drivers/ubd_user.c
|
||||
I810_CARD_MAGIC 0x5072696E i810_card sound/oss/i810_audio.c
|
||||
TRIDENT_CARD_MAGIC 0x5072696E trident_card sound/oss/trident.c
|
||||
ROUTER_MAGIC 0x524d4157 wan_device include/linux/wanrouter.h
|
||||
SCC_MAGIC 0x52696368 gs_port drivers/char/scc.h
|
||||
SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c
|
||||
GDA_MAGIC 0x58464552 gda arch/mips/include/asm/sn/gda.h
|
||||
RED_MAGIC1 0x5a2cf071 (any) mm/slab.c
|
||||
STL_PORTMAGIC 0x5a7182c9 stlport include/linux/stallion.h
|
||||
EEPROM_MAGIC_VALUE 0x5ab478d2 lanai_dev drivers/atm/lanai.c
|
||||
HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state include/linux/hdlcdrv.h
|
||||
EPCA_MAGIC 0x5c6df104 channel include/linux/epca.h
|
||||
PCXX_MAGIC 0x5c6df104 channel drivers/char/pcxx.h
|
||||
KV_MAGIC 0x5f4b565f kernel_vars_s arch/mips/include/asm/sn/klkernvars.h
|
||||
I810_STATE_MAGIC 0x63657373 i810_state sound/oss/i810_audio.c
|
||||
TRIDENT_STATE_MAGIC 0x63657373 trient_state sound/oss/trident.c
|
||||
M3_CARD_MAGIC 0x646e6f50 m3_card sound/oss/maestro3.c
|
||||
FW_HEADER_MAGIC 0x65726F66 fw_header drivers/atm/fore200e.h
|
||||
SLOT_MAGIC 0x67267321 slot drivers/hotplug/cpqphp.h
|
||||
SLOT_MAGIC 0x67267322 slot drivers/hotplug/acpiphp.h
|
||||
LO_MAGIC 0x68797548 nbd_device include/linux/nbd.h
|
||||
OPROFILE_MAGIC 0x6f70726f super_block drivers/oprofile/oprofilefs.h
|
||||
M3_STATE_MAGIC 0x734d724d m3_state sound/oss/maestro3.c
|
||||
STL_PANELMAGIC 0x7ef621a1 stlpanel include/linux/stallion.h
|
||||
VMALLOC_MAGIC 0x87654320 snd_alloc_track sound/core/memory.c
|
||||
KMALLOC_MAGIC 0x87654321 snd_alloc_track sound/core/memory.c
|
||||
PWC_MAGIC 0x89DC10AB pwc_device drivers/usb/media/pwc.h
|
||||
NBD_REPLY_MAGIC 0x96744668 nbd_reply include/linux/nbd.h
|
||||
STL_BOARDMAGIC 0xa2267f52 stlbrd include/linux/stallion.h
|
||||
ENI155_MAGIC 0xa54b872d midway_eprom drivers/atm/eni.h
|
||||
SCI_MAGIC 0xbabeface gs_port drivers/char/sh-sci.h
|
||||
CODA_MAGIC 0xC0DAC0DA coda_file_info include/linux/coda_fs_i.h
|
||||
DPMEM_MAGIC 0xc0ffee11 gdt_pci_sram drivers/scsi/gdth.h
|
||||
STLI_PORTMAGIC 0xe671c7a1 stliport include/linux/istallion.h
|
||||
YAM_MAGIC 0xF10A7654 yam_port drivers/net/hamradio/yam.c
|
||||
CCB_MAGIC 0xf2691ad2 ccb drivers/scsi/ncr53c8xx.c
|
||||
QUEUE_MAGIC_FREE 0xf7e1c9a3 queue_entry drivers/scsi/arm/queue.c
|
||||
QUEUE_MAGIC_USED 0xf7e1cc33 queue_entry drivers/scsi/arm/queue.c
|
||||
HTB_CMAGIC 0xFEFAFEF1 htb_class net/sched/sch_htb.c
|
||||
NMI_MAGIC 0x48414d4d455201 nmi_s arch/mips/include/asm/sn/nmi.h
|
||||
|
||||
请注意,在声音记忆管理中仍然有每一些被定义的驱动魔术值。查看include/sound/sndmagic.h来获取他们完整的列表信息。很多OSS声音驱动拥有自己从声卡PCI ID构建的魔术值-他们也没有被列在这里。
|
||||
|
||||
IrDA子系统也使用了大量的自己的魔术值,查看include/net/irda/irda.h来获取他们完整的信息。
|
||||
|
||||
HFS是另外一个比较大的使用魔术值的文件系统-你可以在fs/hfs/hfs.h中找到他们。
|
@ -17,6 +17,54 @@
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/sparsemem.h>
|
||||
|
||||
static unsigned long get_memblock_size(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned int memblock_size = 0;
|
||||
|
||||
np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
|
||||
if (np) {
|
||||
const unsigned long *size;
|
||||
|
||||
size = of_get_property(np, "ibm,lmb-size", NULL);
|
||||
memblock_size = size ? *size : 0;
|
||||
|
||||
of_node_put(np);
|
||||
} else {
|
||||
unsigned int memzero_size = 0;
|
||||
const unsigned int *regs;
|
||||
|
||||
np = of_find_node_by_path("/memory@0");
|
||||
if (np) {
|
||||
regs = of_get_property(np, "reg", NULL);
|
||||
memzero_size = regs ? regs[3] : 0;
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
if (memzero_size) {
|
||||
/* We now know the size of memory@0, use this to find
|
||||
* the first memoryblock and get its size.
|
||||
*/
|
||||
char buf[64];
|
||||
|
||||
sprintf(buf, "/memory@%x", memzero_size);
|
||||
np = of_find_node_by_path(buf);
|
||||
if (np) {
|
||||
regs = of_get_property(np, "reg", NULL);
|
||||
memblock_size = regs ? regs[3] : 0;
|
||||
of_node_put(np);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return memblock_size;
|
||||
}
|
||||
|
||||
unsigned long memory_block_size_bytes(void)
|
||||
{
|
||||
return get_memblock_size();
|
||||
}
|
||||
|
||||
static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size)
|
||||
{
|
||||
unsigned long start, start_pfn;
|
||||
@ -127,30 +175,22 @@ static int pseries_add_memory(struct device_node *np)
|
||||
|
||||
static int pseries_drconf_memory(unsigned long *base, unsigned int action)
|
||||
{
|
||||
struct device_node *np;
|
||||
const unsigned long *lmb_size;
|
||||
unsigned long memblock_size;
|
||||
int rc;
|
||||
|
||||
np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
|
||||
if (!np)
|
||||
memblock_size = get_memblock_size();
|
||||
if (!memblock_size)
|
||||
return -EINVAL;
|
||||
|
||||
lmb_size = of_get_property(np, "ibm,lmb-size", NULL);
|
||||
if (!lmb_size) {
|
||||
of_node_put(np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (action == PSERIES_DRCONF_MEM_ADD) {
|
||||
rc = memblock_add(*base, *lmb_size);
|
||||
rc = memblock_add(*base, memblock_size);
|
||||
rc = (rc < 0) ? -EINVAL : 0;
|
||||
} else if (action == PSERIES_DRCONF_MEM_REMOVE) {
|
||||
rc = pseries_remove_memblock(*base, *lmb_size);
|
||||
rc = pseries_remove_memblock(*base, memblock_size);
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <asm/numa.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/init.h>
|
||||
#include <asm/uv/uv.h>
|
||||
|
||||
static int __init parse_direct_gbpages_off(char *arg)
|
||||
{
|
||||
@ -898,6 +899,19 @@ const char *arch_vma_name(struct vm_area_struct *vma)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_UV
|
||||
#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
|
||||
|
||||
unsigned long memory_block_size_bytes(void)
|
||||
{
|
||||
if (is_uv_system()) {
|
||||
printk(KERN_INFO "UV: memory block size 2GB\n");
|
||||
return 2UL * 1024 * 1024 * 1024;
|
||||
}
|
||||
return MIN_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
||||
/*
|
||||
* Initialise the sparsemem vmemmap using huge-pages at the PMD level.
|
||||
|
@ -1320,7 +1320,10 @@ struct root_device
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
#define to_root_device(dev) container_of(dev, struct root_device, dev)
|
||||
inline struct root_device *to_root_device(struct device *d)
|
||||
{
|
||||
return container_of(d, struct root_device, dev);
|
||||
}
|
||||
|
||||
static void root_device_release(struct device *dev)
|
||||
{
|
||||
@ -1551,7 +1554,34 @@ EXPORT_SYMBOL_GPL(device_destroy);
|
||||
* on the same device to ensure that new_name is valid and
|
||||
* won't conflict with other devices.
|
||||
*
|
||||
* "Never use this function, bad things will happen" - gregkh
|
||||
* Note: Don't call this function. Currently, the networking layer calls this
|
||||
* function, but that will change. The following text from Kay Sievers offers
|
||||
* some insight:
|
||||
*
|
||||
* Renaming devices is racy at many levels, symlinks and other stuff are not
|
||||
* replaced atomically, and you get a "move" uevent, but it's not easy to
|
||||
* connect the event to the old and new device. Device nodes are not renamed at
|
||||
* all, there isn't even support for that in the kernel now.
|
||||
*
|
||||
* In the meantime, during renaming, your target name might be taken by another
|
||||
* driver, creating conflicts. Or the old name is taken directly after you
|
||||
* renamed it -- then you get events for the same DEVPATH, before you even see
|
||||
* the "move" event. It's just a mess, and nothing new should ever rely on
|
||||
* kernel device renaming. Besides that, it's not even implemented now for
|
||||
* other things than (driver-core wise very simple) network devices.
|
||||
*
|
||||
* We are currently about to change network renaming in udev to completely
|
||||
* disallow renaming of devices in the same namespace as the kernel uses,
|
||||
* because we can't solve the problems properly, that arise with swapping names
|
||||
* of multiple interfaces without races. Means, renaming of eth[0-9]* will only
|
||||
* be allowed to some other name than eth[0-9]*, for the aforementioned
|
||||
* reasons.
|
||||
*
|
||||
* Make up a "real" name in the driver before you register anything, or add
|
||||
* some other attributes for userspace to find the device, or use udev to add
|
||||
* symlinks -- but never rename kernel devices later, it's a complete mess. We
|
||||
* don't even want to get into that and try to implement the missing pieces in
|
||||
* the core. We really have other pieces to fix in the driver core mess. :)
|
||||
*/
|
||||
int device_rename(struct device *dev, const char *new_name)
|
||||
{
|
||||
|
@ -593,8 +593,7 @@ int
|
||||
request_firmware(const struct firmware **firmware_p, const char *name,
|
||||
struct device *device)
|
||||
{
|
||||
int uevent = 1;
|
||||
return _request_firmware(firmware_p, name, device, uevent, false);
|
||||
return _request_firmware(firmware_p, name, device, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -618,7 +617,7 @@ struct firmware_work {
|
||||
struct device *device;
|
||||
void *context;
|
||||
void (*cont)(const struct firmware *fw, void *context);
|
||||
int uevent;
|
||||
bool uevent;
|
||||
};
|
||||
|
||||
static int request_firmware_work_func(void *arg)
|
||||
@ -661,7 +660,7 @@ static int request_firmware_work_func(void *arg)
|
||||
**/
|
||||
int
|
||||
request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
struct module *module, bool uevent,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
|
@ -30,6 +30,14 @@
|
||||
static DEFINE_MUTEX(mem_sysfs_mutex);
|
||||
|
||||
#define MEMORY_CLASS_NAME "memory"
|
||||
#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
|
||||
|
||||
static int sections_per_block;
|
||||
|
||||
static inline int base_memory_block_id(int section_nr)
|
||||
{
|
||||
return section_nr / sections_per_block;
|
||||
}
|
||||
|
||||
static struct sysdev_class memory_sysdev_class = {
|
||||
.name = MEMORY_CLASS_NAME,
|
||||
@ -84,39 +92,72 @@ EXPORT_SYMBOL(unregister_memory_isolate_notifier);
|
||||
* register_memory - Setup a sysfs device for a memory block
|
||||
*/
|
||||
static
|
||||
int register_memory(struct memory_block *memory, struct mem_section *section)
|
||||
int register_memory(struct memory_block *memory)
|
||||
{
|
||||
int error;
|
||||
|
||||
memory->sysdev.cls = &memory_sysdev_class;
|
||||
memory->sysdev.id = __section_nr(section);
|
||||
memory->sysdev.id = memory->start_section_nr / sections_per_block;
|
||||
|
||||
error = sysdev_register(&memory->sysdev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_memory(struct memory_block *memory, struct mem_section *section)
|
||||
unregister_memory(struct memory_block *memory)
|
||||
{
|
||||
BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
|
||||
BUG_ON(memory->sysdev.id != __section_nr(section));
|
||||
|
||||
/* drop the ref. we got in remove_memory_block() */
|
||||
kobject_put(&memory->sysdev.kobj);
|
||||
sysdev_unregister(&memory->sysdev);
|
||||
}
|
||||
|
||||
unsigned long __weak memory_block_size_bytes(void)
|
||||
{
|
||||
return MIN_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static unsigned long get_memory_block_size(void)
|
||||
{
|
||||
unsigned long block_sz;
|
||||
|
||||
block_sz = memory_block_size_bytes();
|
||||
|
||||
/* Validate blk_sz is a power of 2 and not less than section size */
|
||||
if ((block_sz & (block_sz - 1)) || (block_sz < MIN_MEMORY_BLOCK_SIZE)) {
|
||||
WARN_ON(1);
|
||||
block_sz = MIN_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
return block_sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* use this as the physical section index that this memsection
|
||||
* uses.
|
||||
*/
|
||||
|
||||
static ssize_t show_mem_phys_index(struct sys_device *dev,
|
||||
static ssize_t show_mem_start_phys_index(struct sys_device *dev,
|
||||
struct sysdev_attribute *attr, char *buf)
|
||||
{
|
||||
struct memory_block *mem =
|
||||
container_of(dev, struct memory_block, sysdev);
|
||||
return sprintf(buf, "%08lx\n", mem->phys_index);
|
||||
unsigned long phys_index;
|
||||
|
||||
phys_index = mem->start_section_nr / sections_per_block;
|
||||
return sprintf(buf, "%08lx\n", phys_index);
|
||||
}
|
||||
|
||||
static ssize_t show_mem_end_phys_index(struct sys_device *dev,
|
||||
struct sysdev_attribute *attr, char *buf)
|
||||
{
|
||||
struct memory_block *mem =
|
||||
container_of(dev, struct memory_block, sysdev);
|
||||
unsigned long phys_index;
|
||||
|
||||
phys_index = mem->end_section_nr / sections_per_block;
|
||||
return sprintf(buf, "%08lx\n", phys_index);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -125,13 +166,16 @@ static ssize_t show_mem_phys_index(struct sys_device *dev,
|
||||
static ssize_t show_mem_removable(struct sys_device *dev,
|
||||
struct sysdev_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned long start_pfn;
|
||||
int ret;
|
||||
unsigned long i, pfn;
|
||||
int ret = 1;
|
||||
struct memory_block *mem =
|
||||
container_of(dev, struct memory_block, sysdev);
|
||||
|
||||
start_pfn = section_nr_to_pfn(mem->phys_index);
|
||||
ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
|
||||
for (i = 0; i < sections_per_block; i++) {
|
||||
pfn = section_nr_to_pfn(mem->start_section_nr + i);
|
||||
ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
@ -184,17 +228,14 @@ int memory_isolate_notify(unsigned long val, void *v)
|
||||
* OK to have direct references to sparsemem variables in here.
|
||||
*/
|
||||
static int
|
||||
memory_block_action(struct memory_block *mem, unsigned long action)
|
||||
memory_section_action(unsigned long phys_index, unsigned long action)
|
||||
{
|
||||
int i;
|
||||
unsigned long psection;
|
||||
unsigned long start_pfn, start_paddr;
|
||||
struct page *first_page;
|
||||
int ret;
|
||||
int old_state = mem->state;
|
||||
|
||||
psection = mem->phys_index;
|
||||
first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
|
||||
first_page = pfn_to_page(phys_index << PFN_SECTION_SHIFT);
|
||||
|
||||
/*
|
||||
* The probe routines leave the pages reserved, just
|
||||
@ -207,8 +248,8 @@ memory_block_action(struct memory_block *mem, unsigned long action)
|
||||
continue;
|
||||
|
||||
printk(KERN_WARNING "section number %ld page number %d "
|
||||
"not reserved, was it already online? \n",
|
||||
psection, i);
|
||||
"not reserved, was it already online?\n",
|
||||
phys_index, i);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
@ -219,18 +260,13 @@ memory_block_action(struct memory_block *mem, unsigned long action)
|
||||
ret = online_pages(start_pfn, PAGES_PER_SECTION);
|
||||
break;
|
||||
case MEM_OFFLINE:
|
||||
mem->state = MEM_GOING_OFFLINE;
|
||||
start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
|
||||
ret = remove_memory(start_paddr,
|
||||
PAGES_PER_SECTION << PAGE_SHIFT);
|
||||
if (ret) {
|
||||
mem->state = old_state;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
|
||||
__func__, mem, action, action);
|
||||
WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: "
|
||||
"%ld\n", __func__, phys_index, action, action);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
@ -240,7 +276,8 @@ memory_block_action(struct memory_block *mem, unsigned long action)
|
||||
static int memory_block_change_state(struct memory_block *mem,
|
||||
unsigned long to_state, unsigned long from_state_req)
|
||||
{
|
||||
int ret = 0;
|
||||
int i, ret = 0;
|
||||
|
||||
mutex_lock(&mem->state_mutex);
|
||||
|
||||
if (mem->state != from_state_req) {
|
||||
@ -248,8 +285,23 @@ static int memory_block_change_state(struct memory_block *mem,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = memory_block_action(mem, to_state);
|
||||
if (!ret)
|
||||
if (to_state == MEM_OFFLINE)
|
||||
mem->state = MEM_GOING_OFFLINE;
|
||||
|
||||
for (i = 0; i < sections_per_block; i++) {
|
||||
ret = memory_section_action(mem->start_section_nr + i,
|
||||
to_state);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
for (i = 0; i < sections_per_block; i++)
|
||||
memory_section_action(mem->start_section_nr + i,
|
||||
from_state_req);
|
||||
|
||||
mem->state = from_state_req;
|
||||
} else
|
||||
mem->state = to_state;
|
||||
|
||||
out:
|
||||
@ -262,20 +314,15 @@ store_mem_state(struct sys_device *dev,
|
||||
struct sysdev_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct memory_block *mem;
|
||||
unsigned int phys_section_nr;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mem = container_of(dev, struct memory_block, sysdev);
|
||||
phys_section_nr = mem->phys_index;
|
||||
|
||||
if (!present_section_nr(phys_section_nr))
|
||||
goto out;
|
||||
|
||||
if (!strncmp(buf, "online", min((int)count, 6)))
|
||||
ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
|
||||
else if(!strncmp(buf, "offline", min((int)count, 7)))
|
||||
ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
|
||||
out:
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
return count;
|
||||
@ -298,7 +345,8 @@ static ssize_t show_phys_device(struct sys_device *dev,
|
||||
return sprintf(buf, "%d\n", mem->phys_device);
|
||||
}
|
||||
|
||||
static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
|
||||
static SYSDEV_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL);
|
||||
static SYSDEV_ATTR(end_phys_index, 0444, show_mem_end_phys_index, NULL);
|
||||
static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
|
||||
static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
|
||||
static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
|
||||
@ -315,7 +363,7 @@ static ssize_t
|
||||
print_block_size(struct sysdev_class *class, struct sysdev_class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE);
|
||||
return sprintf(buf, "%lx\n", get_memory_block_size());
|
||||
}
|
||||
|
||||
static SYSDEV_CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL);
|
||||
@ -339,12 +387,19 @@ memory_probe_store(struct class *class, struct class_attribute *attr,
|
||||
{
|
||||
u64 phys_addr;
|
||||
int nid;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
phys_addr = simple_strtoull(buf, NULL, 0);
|
||||
|
||||
nid = memory_add_physaddr_to_nid(phys_addr);
|
||||
ret = add_memory(nid, phys_addr, PAGES_PER_SECTION << PAGE_SHIFT);
|
||||
for (i = 0; i < sections_per_block; i++) {
|
||||
nid = memory_add_physaddr_to_nid(phys_addr);
|
||||
ret = add_memory(nid, phys_addr,
|
||||
PAGES_PER_SECTION << PAGE_SHIFT);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
phys_addr += MIN_MEMORY_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
count = ret;
|
||||
@ -444,6 +499,7 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section,
|
||||
struct sys_device *sysdev;
|
||||
struct memory_block *mem;
|
||||
char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
|
||||
int block_id = base_memory_block_id(__section_nr(section));
|
||||
|
||||
kobj = hint ? &hint->sysdev.kobj : NULL;
|
||||
|
||||
@ -451,7 +507,7 @@ struct memory_block *find_memory_block_hinted(struct mem_section *section,
|
||||
* This only works because we know that section == sysdev->id
|
||||
* slightly redundant with sysdev_register()
|
||||
*/
|
||||
sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
|
||||
sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id);
|
||||
|
||||
kobj = kset_find_obj_hinted(&memory_sysdev_class.kset, name, kobj);
|
||||
if (!kobj)
|
||||
@ -476,36 +532,62 @@ struct memory_block *find_memory_block(struct mem_section *section)
|
||||
return find_memory_block_hinted(section, NULL);
|
||||
}
|
||||
|
||||
static int add_memory_block(int nid, struct mem_section *section,
|
||||
unsigned long state, enum mem_add_context context)
|
||||
static int init_memory_block(struct memory_block **memory,
|
||||
struct mem_section *section, unsigned long state)
|
||||
{
|
||||
struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
|
||||
struct memory_block *mem;
|
||||
unsigned long start_pfn;
|
||||
int scn_nr;
|
||||
int ret = 0;
|
||||
|
||||
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
|
||||
if (!mem)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&mem_sysfs_mutex);
|
||||
|
||||
mem->phys_index = __section_nr(section);
|
||||
scn_nr = __section_nr(section);
|
||||
mem->start_section_nr =
|
||||
base_memory_block_id(scn_nr) * sections_per_block;
|
||||
mem->end_section_nr = mem->start_section_nr + sections_per_block - 1;
|
||||
mem->state = state;
|
||||
mem->section_count++;
|
||||
mutex_init(&mem->state_mutex);
|
||||
start_pfn = section_nr_to_pfn(mem->phys_index);
|
||||
start_pfn = section_nr_to_pfn(mem->start_section_nr);
|
||||
mem->phys_device = arch_get_memory_phys_device(start_pfn);
|
||||
|
||||
ret = register_memory(mem, section);
|
||||
ret = register_memory(mem);
|
||||
if (!ret)
|
||||
ret = mem_create_simple_file(mem, phys_index);
|
||||
if (!ret)
|
||||
ret = mem_create_simple_file(mem, end_phys_index);
|
||||
if (!ret)
|
||||
ret = mem_create_simple_file(mem, state);
|
||||
if (!ret)
|
||||
ret = mem_create_simple_file(mem, phys_device);
|
||||
if (!ret)
|
||||
ret = mem_create_simple_file(mem, removable);
|
||||
|
||||
*memory = mem;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int add_memory_section(int nid, struct mem_section *section,
|
||||
unsigned long state, enum mem_add_context context)
|
||||
{
|
||||
struct memory_block *mem;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&mem_sysfs_mutex);
|
||||
|
||||
mem = find_memory_block(section);
|
||||
if (mem) {
|
||||
mem->section_count++;
|
||||
kobject_put(&mem->sysdev.kobj);
|
||||
} else
|
||||
ret = init_memory_block(&mem, section, state);
|
||||
|
||||
if (!ret) {
|
||||
if (context == HOTPLUG)
|
||||
if (context == HOTPLUG &&
|
||||
mem->section_count == sections_per_block)
|
||||
ret = register_mem_sect_under_node(mem, nid);
|
||||
}
|
||||
|
||||
@ -520,16 +602,19 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
|
||||
|
||||
mutex_lock(&mem_sysfs_mutex);
|
||||
mem = find_memory_block(section);
|
||||
unregister_mem_sect_under_nodes(mem, __section_nr(section));
|
||||
|
||||
mem->section_count--;
|
||||
if (mem->section_count == 0) {
|
||||
unregister_mem_sect_under_nodes(mem);
|
||||
mem_remove_simple_file(mem, phys_index);
|
||||
mem_remove_simple_file(mem, end_phys_index);
|
||||
mem_remove_simple_file(mem, state);
|
||||
mem_remove_simple_file(mem, phys_device);
|
||||
mem_remove_simple_file(mem, removable);
|
||||
unregister_memory(mem, section);
|
||||
}
|
||||
unregister_memory(mem);
|
||||
kfree(mem);
|
||||
} else
|
||||
kobject_put(&mem->sysdev.kobj);
|
||||
|
||||
mutex_unlock(&mem_sysfs_mutex);
|
||||
return 0;
|
||||
@ -541,7 +626,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
|
||||
*/
|
||||
int register_new_memory(int nid, struct mem_section *section)
|
||||
{
|
||||
return add_memory_block(nid, section, MEM_OFFLINE, HOTPLUG);
|
||||
return add_memory_section(nid, section, MEM_OFFLINE, HOTPLUG);
|
||||
}
|
||||
|
||||
int unregister_memory_section(struct mem_section *section)
|
||||
@ -560,12 +645,16 @@ int __init memory_dev_init(void)
|
||||
unsigned int i;
|
||||
int ret;
|
||||
int err;
|
||||
unsigned long block_sz;
|
||||
|
||||
memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
|
||||
ret = sysdev_class_register(&memory_sysdev_class);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
block_sz = get_memory_block_size();
|
||||
sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
|
||||
|
||||
/*
|
||||
* Create entries for memory sections that were found
|
||||
* during boot and have been initialized
|
||||
@ -573,8 +662,8 @@ int __init memory_dev_init(void)
|
||||
for (i = 0; i < NR_MEM_SECTIONS; i++) {
|
||||
if (!present_section_nr(i))
|
||||
continue;
|
||||
err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE,
|
||||
BOOT);
|
||||
err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE,
|
||||
BOOT);
|
||||
if (!ret)
|
||||
ret = err;
|
||||
}
|
||||
|
@ -375,8 +375,10 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
|
||||
return -EFAULT;
|
||||
if (!node_online(nid))
|
||||
return 0;
|
||||
sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
|
||||
sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
|
||||
|
||||
sect_start_pfn = section_nr_to_pfn(mem_blk->start_section_nr);
|
||||
sect_end_pfn = section_nr_to_pfn(mem_blk->end_section_nr);
|
||||
sect_end_pfn += PAGES_PER_SECTION - 1;
|
||||
for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
|
||||
int page_nid;
|
||||
|
||||
@ -400,7 +402,8 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid)
|
||||
}
|
||||
|
||||
/* unregister memory section under all nodes that it spans */
|
||||
int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
|
||||
int unregister_mem_sect_under_nodes(struct memory_block *mem_blk,
|
||||
unsigned long phys_index)
|
||||
{
|
||||
NODEMASK_ALLOC(nodemask_t, unlinked_nodes, GFP_KERNEL);
|
||||
unsigned long pfn, sect_start_pfn, sect_end_pfn;
|
||||
@ -412,7 +415,8 @@ int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
|
||||
if (!unlinked_nodes)
|
||||
return -ENOMEM;
|
||||
nodes_clear(*unlinked_nodes);
|
||||
sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
|
||||
|
||||
sect_start_pfn = section_nr_to_pfn(phys_index);
|
||||
sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
|
||||
for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
|
||||
int nid;
|
||||
|
@ -166,6 +166,36 @@ EXPORT_SYMBOL_GPL(sysdev_class_unregister);
|
||||
|
||||
static DEFINE_MUTEX(sysdev_drivers_lock);
|
||||
|
||||
/*
|
||||
* @dev != NULL means that we're unwinding because some drv->add()
|
||||
* failed for some reason. You need to grab sysdev_drivers_lock before
|
||||
* calling this.
|
||||
*/
|
||||
static void __sysdev_driver_remove(struct sysdev_class *cls,
|
||||
struct sysdev_driver *drv,
|
||||
struct sys_device *from_dev)
|
||||
{
|
||||
struct sys_device *dev = from_dev;
|
||||
|
||||
list_del_init(&drv->entry);
|
||||
if (!cls)
|
||||
return;
|
||||
|
||||
if (!drv->remove)
|
||||
goto kset_put;
|
||||
|
||||
if (dev)
|
||||
list_for_each_entry_continue_reverse(dev, &cls->kset.list,
|
||||
kobj.entry)
|
||||
drv->remove(dev);
|
||||
else
|
||||
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
|
||||
drv->remove(dev);
|
||||
|
||||
kset_put:
|
||||
kset_put(&cls->kset);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysdev_driver_register - Register auxillary driver
|
||||
* @cls: Device class driver belongs to.
|
||||
@ -175,14 +205,14 @@ static DEFINE_MUTEX(sysdev_drivers_lock);
|
||||
* called on each operation on devices of that class. The refcount
|
||||
* of @cls is incremented.
|
||||
*/
|
||||
|
||||
int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
|
||||
{
|
||||
struct sys_device *dev = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (!cls) {
|
||||
WARN(1, KERN_WARNING "sysdev: invalid class passed to "
|
||||
"sysdev_driver_register!\n");
|
||||
WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -198,19 +228,27 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
|
||||
|
||||
/* If devices of this class already exist, tell the driver */
|
||||
if (drv->add) {
|
||||
struct sys_device *dev;
|
||||
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
|
||||
drv->add(dev);
|
||||
list_for_each_entry(dev, &cls->kset.list, kobj.entry) {
|
||||
err = drv->add(dev);
|
||||
if (err)
|
||||
goto unwind;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
|
||||
}
|
||||
|
||||
goto unlock;
|
||||
|
||||
unwind:
|
||||
__sysdev_driver_remove(cls, drv, dev);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&sysdev_drivers_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sysdev_driver_unregister - Remove an auxillary driver.
|
||||
* @cls: Class driver belongs to.
|
||||
@ -220,23 +258,12 @@ void sysdev_driver_unregister(struct sysdev_class *cls,
|
||||
struct sysdev_driver *drv)
|
||||
{
|
||||
mutex_lock(&sysdev_drivers_lock);
|
||||
list_del_init(&drv->entry);
|
||||
if (cls) {
|
||||
if (drv->remove) {
|
||||
struct sys_device *dev;
|
||||
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
|
||||
drv->remove(dev);
|
||||
}
|
||||
kset_put(&cls->kset);
|
||||
}
|
||||
__sysdev_driver_remove(cls, drv, NULL);
|
||||
mutex_unlock(&sysdev_drivers_lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(sysdev_driver_register);
|
||||
EXPORT_SYMBOL_GPL(sysdev_driver_unregister);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* sysdev_register - add a system device to the tree
|
||||
* @sysdev: device in question
|
||||
|
@ -113,6 +113,17 @@ config DMIID
|
||||
information from userspace through /sys/class/dmi/id/ or if you want
|
||||
DMI-based module auto-loading.
|
||||
|
||||
config DMI_SYSFS
|
||||
tristate "DMI table support in sysfs"
|
||||
depends on SYSFS && DMI
|
||||
default n
|
||||
help
|
||||
Say Y or M here to enable the exporting of the raw DMI table
|
||||
data via sysfs. This is useful for consuming the data without
|
||||
requiring any access to /dev/mem at all. Tables are found
|
||||
under /sys/firmware/dmi when this option is enabled and
|
||||
loaded.
|
||||
|
||||
config ISCSI_IBFT_FIND
|
||||
bool "iSCSI Boot Firmware Table Attributes"
|
||||
depends on X86
|
||||
|
@ -2,6 +2,7 @@
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
obj-$(CONFIG_DMI) += dmi_scan.o
|
||||
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
|
||||
obj-$(CONFIG_EDD) += edd.o
|
||||
obj-$(CONFIG_EFI_VARS) += efivars.o
|
||||
obj-$(CONFIG_EFI_PCDP) += pcdp.o
|
||||
|
696
drivers/firmware/dmi-sysfs.c
Normal file
696
drivers/firmware/dmi-sysfs.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* dmi-sysfs.c
|
||||
*
|
||||
* This module exports the DMI tables read-only to userspace through the
|
||||
* sysfs file system.
|
||||
*
|
||||
* Data is currently found below
|
||||
* /sys/firmware/dmi/...
|
||||
*
|
||||
* DMI attributes are presented in attribute files with names
|
||||
* formatted using %d-%d, so that the first integer indicates the
|
||||
* structure type (0-255), and the second field is the instance of that
|
||||
* entry.
|
||||
*
|
||||
* Copyright 2011 Google, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define MAX_ENTRY_TYPE 255 /* Most of these aren't used, but we consider
|
||||
the top entry type is only 8 bits */
|
||||
|
||||
struct dmi_sysfs_entry {
|
||||
struct dmi_header dh;
|
||||
struct kobject kobj;
|
||||
int instance;
|
||||
int position;
|
||||
struct list_head list;
|
||||
struct kobject *child;
|
||||
};
|
||||
|
||||
/*
|
||||
* Global list of dmi_sysfs_entry. Even though this should only be
|
||||
* manipulated at setup and teardown, the lazy nature of the kobject
|
||||
* system means we get lazy removes.
|
||||
*/
|
||||
static LIST_HEAD(entry_list);
|
||||
static DEFINE_SPINLOCK(entry_list_lock);
|
||||
|
||||
/* dmi_sysfs_attribute - Top level attribute. used by all entries. */
|
||||
struct dmi_sysfs_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct dmi_sysfs_entry *entry, char *buf);
|
||||
};
|
||||
|
||||
#define DMI_SYSFS_ATTR(_entry, _name) \
|
||||
struct dmi_sysfs_attribute dmi_sysfs_attr_##_entry##_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = 0400}, \
|
||||
.show = dmi_sysfs_##_entry##_##_name, \
|
||||
}
|
||||
|
||||
/*
|
||||
* dmi_sysfs_mapped_attribute - Attribute where we require the entry be
|
||||
* mapped in. Use in conjunction with dmi_sysfs_specialize_attr_ops.
|
||||
*/
|
||||
struct dmi_sysfs_mapped_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_header *dh,
|
||||
char *buf);
|
||||
};
|
||||
|
||||
#define DMI_SYSFS_MAPPED_ATTR(_entry, _name) \
|
||||
struct dmi_sysfs_mapped_attribute dmi_sysfs_attr_##_entry##_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = 0400}, \
|
||||
.show = dmi_sysfs_##_entry##_##_name, \
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Generic DMI entry support.
|
||||
*************************************************/
|
||||
static void dmi_entry_free(struct kobject *kobj)
|
||||
{
|
||||
kfree(kobj);
|
||||
}
|
||||
|
||||
static struct dmi_sysfs_entry *to_entry(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct dmi_sysfs_entry, kobj);
|
||||
}
|
||||
|
||||
static struct dmi_sysfs_attribute *to_attr(struct attribute *attr)
|
||||
{
|
||||
return container_of(attr, struct dmi_sysfs_attribute, attr);
|
||||
}
|
||||
|
||||
static ssize_t dmi_sysfs_attr_show(struct kobject *kobj,
|
||||
struct attribute *_attr, char *buf)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry = to_entry(kobj);
|
||||
struct dmi_sysfs_attribute *attr = to_attr(_attr);
|
||||
|
||||
/* DMI stuff is only ever admin visible */
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
return attr->show(entry, buf);
|
||||
}
|
||||
|
||||
static const struct sysfs_ops dmi_sysfs_attr_ops = {
|
||||
.show = dmi_sysfs_attr_show,
|
||||
};
|
||||
|
||||
typedef ssize_t (*dmi_callback)(struct dmi_sysfs_entry *,
|
||||
const struct dmi_header *dh, void *);
|
||||
|
||||
struct find_dmi_data {
|
||||
struct dmi_sysfs_entry *entry;
|
||||
dmi_callback callback;
|
||||
void *private;
|
||||
int instance_countdown;
|
||||
ssize_t ret;
|
||||
};
|
||||
|
||||
static void find_dmi_entry_helper(const struct dmi_header *dh,
|
||||
void *_data)
|
||||
{
|
||||
struct find_dmi_data *data = _data;
|
||||
struct dmi_sysfs_entry *entry = data->entry;
|
||||
|
||||
/* Is this the entry we want? */
|
||||
if (dh->type != entry->dh.type)
|
||||
return;
|
||||
|
||||
if (data->instance_countdown != 0) {
|
||||
/* try the next instance? */
|
||||
data->instance_countdown--;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't ever revisit the instance. Short circuit later
|
||||
* instances by letting the instance_countdown run negative
|
||||
*/
|
||||
data->instance_countdown--;
|
||||
|
||||
/* Found the entry */
|
||||
data->ret = data->callback(entry, dh, data->private);
|
||||
}
|
||||
|
||||
/* State for passing the read parameters through dmi_find_entry() */
|
||||
struct dmi_read_state {
|
||||
char *buf;
|
||||
loff_t pos;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
static ssize_t find_dmi_entry(struct dmi_sysfs_entry *entry,
|
||||
dmi_callback callback, void *private)
|
||||
{
|
||||
struct find_dmi_data data = {
|
||||
.entry = entry,
|
||||
.callback = callback,
|
||||
.private = private,
|
||||
.instance_countdown = entry->instance,
|
||||
.ret = -EIO, /* To signal the entry disappeared */
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = dmi_walk(find_dmi_entry_helper, &data);
|
||||
/* This shouldn't happen, but just in case. */
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate and return the byte length of the dmi entry identified by
|
||||
* dh. This includes both the formatted portion as well as the
|
||||
* unformatted string space, including the two trailing nul characters.
|
||||
*/
|
||||
static size_t dmi_entry_length(const struct dmi_header *dh)
|
||||
{
|
||||
const char *p = (const char *)dh;
|
||||
|
||||
p += dh->length;
|
||||
|
||||
while (p[0] || p[1])
|
||||
p++;
|
||||
|
||||
return 2 + p - (const char *)dh;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Support bits for specialized DMI entry support
|
||||
*************************************************/
|
||||
struct dmi_entry_attr_show_data {
|
||||
struct attribute *attr;
|
||||
char *buf;
|
||||
};
|
||||
|
||||
static ssize_t dmi_entry_attr_show_helper(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_header *dh,
|
||||
void *_data)
|
||||
{
|
||||
struct dmi_entry_attr_show_data *data = _data;
|
||||
struct dmi_sysfs_mapped_attribute *attr;
|
||||
|
||||
attr = container_of(data->attr,
|
||||
struct dmi_sysfs_mapped_attribute, attr);
|
||||
return attr->show(entry, dh, data->buf);
|
||||
}
|
||||
|
||||
static ssize_t dmi_entry_attr_show(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dmi_entry_attr_show_data data = {
|
||||
.attr = attr,
|
||||
.buf = buf,
|
||||
};
|
||||
/* Find the entry according to our parent and call the
|
||||
* normalized show method hanging off of the attribute */
|
||||
return find_dmi_entry(to_entry(kobj->parent),
|
||||
dmi_entry_attr_show_helper, &data);
|
||||
}
|
||||
|
||||
static const struct sysfs_ops dmi_sysfs_specialize_attr_ops = {
|
||||
.show = dmi_entry_attr_show,
|
||||
};
|
||||
|
||||
/*************************************************
|
||||
* Specialized DMI entry support.
|
||||
*************************************************/
|
||||
|
||||
/*** Type 15 - System Event Table ***/
|
||||
|
||||
#define DMI_SEL_ACCESS_METHOD_IO8 0x00
|
||||
#define DMI_SEL_ACCESS_METHOD_IO2x8 0x01
|
||||
#define DMI_SEL_ACCESS_METHOD_IO16 0x02
|
||||
#define DMI_SEL_ACCESS_METHOD_PHYS32 0x03
|
||||
#define DMI_SEL_ACCESS_METHOD_GPNV 0x04
|
||||
|
||||
struct dmi_system_event_log {
|
||||
struct dmi_header header;
|
||||
u16 area_length;
|
||||
u16 header_start_offset;
|
||||
u16 data_start_offset;
|
||||
u8 access_method;
|
||||
u8 status;
|
||||
u32 change_token;
|
||||
union {
|
||||
struct {
|
||||
u16 index_addr;
|
||||
u16 data_addr;
|
||||
} io;
|
||||
u32 phys_addr32;
|
||||
u16 gpnv_handle;
|
||||
u32 access_method_address;
|
||||
};
|
||||
u8 header_format;
|
||||
u8 type_descriptors_supported_count;
|
||||
u8 per_log_type_descriptor_length;
|
||||
u8 supported_log_type_descriptos[0];
|
||||
} __packed;
|
||||
|
||||
#define DMI_SYSFS_SEL_FIELD(_field) \
|
||||
static ssize_t dmi_sysfs_sel_##_field(struct dmi_sysfs_entry *entry, \
|
||||
const struct dmi_header *dh, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct dmi_system_event_log sel; \
|
||||
if (sizeof(sel) > dmi_entry_length(dh)) \
|
||||
return -EIO; \
|
||||
memcpy(&sel, dh, sizeof(sel)); \
|
||||
return sprintf(buf, "%u\n", sel._field); \
|
||||
} \
|
||||
static DMI_SYSFS_MAPPED_ATTR(sel, _field)
|
||||
|
||||
DMI_SYSFS_SEL_FIELD(area_length);
|
||||
DMI_SYSFS_SEL_FIELD(header_start_offset);
|
||||
DMI_SYSFS_SEL_FIELD(data_start_offset);
|
||||
DMI_SYSFS_SEL_FIELD(access_method);
|
||||
DMI_SYSFS_SEL_FIELD(status);
|
||||
DMI_SYSFS_SEL_FIELD(change_token);
|
||||
DMI_SYSFS_SEL_FIELD(access_method_address);
|
||||
DMI_SYSFS_SEL_FIELD(header_format);
|
||||
DMI_SYSFS_SEL_FIELD(type_descriptors_supported_count);
|
||||
DMI_SYSFS_SEL_FIELD(per_log_type_descriptor_length);
|
||||
|
||||
static struct attribute *dmi_sysfs_sel_attrs[] = {
|
||||
&dmi_sysfs_attr_sel_area_length.attr,
|
||||
&dmi_sysfs_attr_sel_header_start_offset.attr,
|
||||
&dmi_sysfs_attr_sel_data_start_offset.attr,
|
||||
&dmi_sysfs_attr_sel_access_method.attr,
|
||||
&dmi_sysfs_attr_sel_status.attr,
|
||||
&dmi_sysfs_attr_sel_change_token.attr,
|
||||
&dmi_sysfs_attr_sel_access_method_address.attr,
|
||||
&dmi_sysfs_attr_sel_header_format.attr,
|
||||
&dmi_sysfs_attr_sel_type_descriptors_supported_count.attr,
|
||||
&dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static struct kobj_type dmi_system_event_log_ktype = {
|
||||
.release = dmi_entry_free,
|
||||
.sysfs_ops = &dmi_sysfs_specialize_attr_ops,
|
||||
.default_attrs = dmi_sysfs_sel_attrs,
|
||||
};
|
||||
|
||||
typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel,
|
||||
loff_t offset);
|
||||
|
||||
static DEFINE_MUTEX(io_port_lock);
|
||||
|
||||
static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel,
|
||||
loff_t offset)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
mutex_lock(&io_port_lock);
|
||||
outb((u8)offset, sel->io.index_addr);
|
||||
ret = inb(sel->io.data_addr);
|
||||
mutex_unlock(&io_port_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel,
|
||||
loff_t offset)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
mutex_lock(&io_port_lock);
|
||||
outb((u8)offset, sel->io.index_addr);
|
||||
outb((u8)(offset >> 8), sel->io.index_addr + 1);
|
||||
ret = inb(sel->io.data_addr);
|
||||
mutex_unlock(&io_port_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel,
|
||||
loff_t offset)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
mutex_lock(&io_port_lock);
|
||||
outw((u16)offset, sel->io.index_addr);
|
||||
ret = inb(sel->io.data_addr);
|
||||
mutex_unlock(&io_port_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static sel_io_reader sel_io_readers[] = {
|
||||
[DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io,
|
||||
[DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io,
|
||||
[DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io,
|
||||
};
|
||||
|
||||
static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_system_event_log *sel,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
ssize_t wrote = 0;
|
||||
|
||||
sel_io_reader io_reader = sel_io_readers[sel->access_method];
|
||||
|
||||
while (count && pos < sel->area_length) {
|
||||
count--;
|
||||
*(buf++) = io_reader(sel, pos++);
|
||||
wrote++;
|
||||
}
|
||||
|
||||
return wrote;
|
||||
}
|
||||
|
||||
static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_system_event_log *sel,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
u8 __iomem *mapped;
|
||||
ssize_t wrote = 0;
|
||||
|
||||
mapped = ioremap(sel->access_method_address, sel->area_length);
|
||||
if (!mapped)
|
||||
return -EIO;
|
||||
|
||||
while (count && pos < sel->area_length) {
|
||||
count--;
|
||||
*(buf++) = readb(mapped + pos++);
|
||||
wrote++;
|
||||
}
|
||||
|
||||
iounmap(mapped);
|
||||
return wrote;
|
||||
}
|
||||
|
||||
static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_header *dh,
|
||||
void *_state)
|
||||
{
|
||||
struct dmi_read_state *state = _state;
|
||||
struct dmi_system_event_log sel;
|
||||
|
||||
if (sizeof(sel) > dmi_entry_length(dh))
|
||||
return -EIO;
|
||||
|
||||
memcpy(&sel, dh, sizeof(sel));
|
||||
|
||||
switch (sel.access_method) {
|
||||
case DMI_SEL_ACCESS_METHOD_IO8:
|
||||
case DMI_SEL_ACCESS_METHOD_IO2x8:
|
||||
case DMI_SEL_ACCESS_METHOD_IO16:
|
||||
return dmi_sel_raw_read_io(entry, &sel, state->buf,
|
||||
state->pos, state->count);
|
||||
case DMI_SEL_ACCESS_METHOD_PHYS32:
|
||||
return dmi_sel_raw_read_phys32(entry, &sel, state->buf,
|
||||
state->pos, state->count);
|
||||
case DMI_SEL_ACCESS_METHOD_GPNV:
|
||||
pr_info("dmi-sysfs: GPNV support missing.\n");
|
||||
return -EIO;
|
||||
default:
|
||||
pr_info("dmi-sysfs: Unknown access method %02x\n",
|
||||
sel.access_method);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry = to_entry(kobj->parent);
|
||||
struct dmi_read_state state = {
|
||||
.buf = buf,
|
||||
.pos = pos,
|
||||
.count = count,
|
||||
};
|
||||
|
||||
return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state);
|
||||
}
|
||||
|
||||
static struct bin_attribute dmi_sel_raw_attr = {
|
||||
.attr = {.name = "raw_event_log", .mode = 0400},
|
||||
.read = dmi_sel_raw_read,
|
||||
};
|
||||
|
||||
static int dmi_system_event_log(struct dmi_sysfs_entry *entry)
|
||||
{
|
||||
int ret;
|
||||
|
||||
entry->child = kzalloc(sizeof(*entry->child), GFP_KERNEL);
|
||||
if (!entry->child)
|
||||
return -ENOMEM;
|
||||
ret = kobject_init_and_add(entry->child,
|
||||
&dmi_system_event_log_ktype,
|
||||
&entry->kobj,
|
||||
"system_event_log");
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr);
|
||||
if (ret)
|
||||
goto out_del;
|
||||
|
||||
return 0;
|
||||
|
||||
out_del:
|
||||
kobject_del(entry->child);
|
||||
out_free:
|
||||
kfree(entry->child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
* Generic DMI entry support.
|
||||
*************************************************/
|
||||
|
||||
static ssize_t dmi_sysfs_entry_length(struct dmi_sysfs_entry *entry, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", entry->dh.length);
|
||||
}
|
||||
|
||||
static ssize_t dmi_sysfs_entry_handle(struct dmi_sysfs_entry *entry, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", entry->dh.handle);
|
||||
}
|
||||
|
||||
static ssize_t dmi_sysfs_entry_type(struct dmi_sysfs_entry *entry, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", entry->dh.type);
|
||||
}
|
||||
|
||||
static ssize_t dmi_sysfs_entry_instance(struct dmi_sysfs_entry *entry,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", entry->instance);
|
||||
}
|
||||
|
||||
static ssize_t dmi_sysfs_entry_position(struct dmi_sysfs_entry *entry,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", entry->position);
|
||||
}
|
||||
|
||||
static DMI_SYSFS_ATTR(entry, length);
|
||||
static DMI_SYSFS_ATTR(entry, handle);
|
||||
static DMI_SYSFS_ATTR(entry, type);
|
||||
static DMI_SYSFS_ATTR(entry, instance);
|
||||
static DMI_SYSFS_ATTR(entry, position);
|
||||
|
||||
static struct attribute *dmi_sysfs_entry_attrs[] = {
|
||||
&dmi_sysfs_attr_entry_length.attr,
|
||||
&dmi_sysfs_attr_entry_handle.attr,
|
||||
&dmi_sysfs_attr_entry_type.attr,
|
||||
&dmi_sysfs_attr_entry_instance.attr,
|
||||
&dmi_sysfs_attr_entry_position.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry,
|
||||
const struct dmi_header *dh,
|
||||
void *_state)
|
||||
{
|
||||
struct dmi_read_state *state = _state;
|
||||
size_t entry_length;
|
||||
|
||||
entry_length = dmi_entry_length(dh);
|
||||
|
||||
return memory_read_from_buffer(state->buf, state->count,
|
||||
&state->pos, dh, entry_length);
|
||||
}
|
||||
|
||||
static ssize_t dmi_entry_raw_read(struct file *filp,
|
||||
struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry = to_entry(kobj);
|
||||
struct dmi_read_state state = {
|
||||
.buf = buf,
|
||||
.pos = pos,
|
||||
.count = count,
|
||||
};
|
||||
|
||||
return find_dmi_entry(entry, dmi_entry_raw_read_helper, &state);
|
||||
}
|
||||
|
||||
static const struct bin_attribute dmi_entry_raw_attr = {
|
||||
.attr = {.name = "raw", .mode = 0400},
|
||||
.read = dmi_entry_raw_read,
|
||||
};
|
||||
|
||||
static void dmi_sysfs_entry_release(struct kobject *kobj)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry = to_entry(kobj);
|
||||
sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr);
|
||||
spin_lock(&entry_list_lock);
|
||||
list_del(&entry->list);
|
||||
spin_unlock(&entry_list_lock);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static struct kobj_type dmi_sysfs_entry_ktype = {
|
||||
.release = dmi_sysfs_entry_release,
|
||||
.sysfs_ops = &dmi_sysfs_attr_ops,
|
||||
.default_attrs = dmi_sysfs_entry_attrs,
|
||||
};
|
||||
|
||||
static struct kobject *dmi_kobj;
|
||||
static struct kset *dmi_kset;
|
||||
|
||||
/* Global count of all instances seen. Only for setup */
|
||||
static int __initdata instance_counts[MAX_ENTRY_TYPE + 1];
|
||||
|
||||
/* Global positional count of all entries seen. Only for setup */
|
||||
static int __initdata position_count;
|
||||
|
||||
static void __init dmi_sysfs_register_handle(const struct dmi_header *dh,
|
||||
void *_ret)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry;
|
||||
int *ret = _ret;
|
||||
|
||||
/* If a previous entry saw an error, short circuit */
|
||||
if (*ret)
|
||||
return;
|
||||
|
||||
/* Allocate and register a new entry into the entries set */
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
*ret = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the key */
|
||||
memcpy(&entry->dh, dh, sizeof(*dh));
|
||||
entry->instance = instance_counts[dh->type]++;
|
||||
entry->position = position_count++;
|
||||
|
||||
entry->kobj.kset = dmi_kset;
|
||||
*ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL,
|
||||
"%d-%d", dh->type, entry->instance);
|
||||
|
||||
if (*ret) {
|
||||
kfree(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Thread on the global list for cleanup */
|
||||
spin_lock(&entry_list_lock);
|
||||
list_add_tail(&entry->list, &entry_list);
|
||||
spin_unlock(&entry_list_lock);
|
||||
|
||||
/* Handle specializations by type */
|
||||
switch (dh->type) {
|
||||
case DMI_ENTRY_SYSTEM_EVENT_LOG:
|
||||
*ret = dmi_system_event_log(entry);
|
||||
break;
|
||||
default:
|
||||
/* No specialization */
|
||||
break;
|
||||
}
|
||||
if (*ret)
|
||||
goto out_err;
|
||||
|
||||
/* Create the raw binary file to access the entry */
|
||||
*ret = sysfs_create_bin_file(&entry->kobj, &dmi_entry_raw_attr);
|
||||
if (*ret)
|
||||
goto out_err;
|
||||
|
||||
return;
|
||||
out_err:
|
||||
kobject_put(entry->child);
|
||||
kobject_put(&entry->kobj);
|
||||
return;
|
||||
}
|
||||
|
||||
static void cleanup_entry_list(void)
|
||||
{
|
||||
struct dmi_sysfs_entry *entry, *next;
|
||||
|
||||
/* No locks, we are on our way out */
|
||||
list_for_each_entry_safe(entry, next, &entry_list, list) {
|
||||
kobject_put(entry->child);
|
||||
kobject_put(&entry->kobj);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init dmi_sysfs_init(void)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
int val;
|
||||
|
||||
/* Set up our directory */
|
||||
dmi_kobj = kobject_create_and_add("dmi", firmware_kobj);
|
||||
if (!dmi_kobj)
|
||||
goto err;
|
||||
|
||||
dmi_kset = kset_create_and_add("entries", NULL, dmi_kobj);
|
||||
if (!dmi_kset)
|
||||
goto err;
|
||||
|
||||
val = 0;
|
||||
error = dmi_walk(dmi_sysfs_register_handle, &val);
|
||||
if (error)
|
||||
goto err;
|
||||
if (val) {
|
||||
error = val;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pr_debug("dmi-sysfs: loaded.\n");
|
||||
|
||||
return 0;
|
||||
err:
|
||||
cleanup_entry_list();
|
||||
kset_unregister(dmi_kset);
|
||||
kobject_put(dmi_kobj);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* clean up everything. */
|
||||
static void __exit dmi_sysfs_exit(void)
|
||||
{
|
||||
pr_debug("dmi-sysfs: unloading.\n");
|
||||
cleanup_entry_list();
|
||||
kset_unregister(dmi_kset);
|
||||
kobject_put(dmi_kobj);
|
||||
}
|
||||
|
||||
module_init(dmi_sysfs_init);
|
||||
module_exit(dmi_sysfs_exit);
|
||||
|
||||
MODULE_AUTHOR("Mike Waychison <mikew@google.com>");
|
||||
MODULE_DESCRIPTION("DMI sysfs support");
|
||||
MODULE_LICENSE("GPL");
|
@ -89,17 +89,6 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(EFIVARS_VERSION);
|
||||
|
||||
/*
|
||||
* efivars_lock protects two things:
|
||||
* 1) efivar_list - adds, removals, reads, writes
|
||||
* 2) efi.[gs]et_variable() calls.
|
||||
* It must not be held when creating sysfs entries or calling kmalloc.
|
||||
* efi.get_next_variable() is only called from efivars_init(),
|
||||
* which is protected by the BKL, so that path is safe.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(efivars_lock);
|
||||
static LIST_HEAD(efivar_list);
|
||||
|
||||
/*
|
||||
* The maximum size of VariableName + Data = 1024
|
||||
* Therefore, it's reasonable to save that much
|
||||
@ -118,6 +107,7 @@ struct efi_variable {
|
||||
|
||||
|
||||
struct efivar_entry {
|
||||
struct efivars *efivars;
|
||||
struct efi_variable var;
|
||||
struct list_head list;
|
||||
struct kobject kobj;
|
||||
@ -144,9 +134,10 @@ struct efivar_attribute efivar_attr_##_name = { \
|
||||
* Prototype for sysfs creation function
|
||||
*/
|
||||
static int
|
||||
efivar_create_sysfs_entry(unsigned long variable_name_size,
|
||||
efi_char16_t *variable_name,
|
||||
efi_guid_t *vendor_guid);
|
||||
efivar_create_sysfs_entry(struct efivars *efivars,
|
||||
unsigned long variable_name_size,
|
||||
efi_char16_t *variable_name,
|
||||
efi_guid_t *vendor_guid);
|
||||
|
||||
/* Return the number of unicode characters in data */
|
||||
static unsigned long
|
||||
@ -170,18 +161,18 @@ utf8_strsize(efi_char16_t *data, unsigned long maxlength)
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
get_var_data(struct efi_variable *var)
|
||||
get_var_data(struct efivars *efivars, struct efi_variable *var)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efivars_lock);
|
||||
spin_lock(&efivars->lock);
|
||||
var->DataSize = 1024;
|
||||
status = efi.get_variable(var->VariableName,
|
||||
&var->VendorGuid,
|
||||
&var->Attributes,
|
||||
&var->DataSize,
|
||||
var->Data);
|
||||
spin_unlock(&efivars_lock);
|
||||
status = efivars->ops->get_variable(var->VariableName,
|
||||
&var->VendorGuid,
|
||||
&var->Attributes,
|
||||
&var->DataSize,
|
||||
var->Data);
|
||||
spin_unlock(&efivars->lock);
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
|
||||
status);
|
||||
@ -215,7 +206,7 @@ efivar_attr_read(struct efivar_entry *entry, char *buf)
|
||||
if (!entry || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
status = get_var_data(var);
|
||||
status = get_var_data(entry->efivars, var);
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
@ -238,7 +229,7 @@ efivar_size_read(struct efivar_entry *entry, char *buf)
|
||||
if (!entry || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
status = get_var_data(var);
|
||||
status = get_var_data(entry->efivars, var);
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
@ -255,7 +246,7 @@ efivar_data_read(struct efivar_entry *entry, char *buf)
|
||||
if (!entry || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
status = get_var_data(var);
|
||||
status = get_var_data(entry->efivars, var);
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
@ -270,6 +261,7 @@ static ssize_t
|
||||
efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
|
||||
{
|
||||
struct efi_variable *new_var, *var = &entry->var;
|
||||
struct efivars *efivars = entry->efivars;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
|
||||
if (count != sizeof(struct efi_variable))
|
||||
@ -291,14 +283,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(&efivars_lock);
|
||||
status = efi.set_variable(new_var->VariableName,
|
||||
&new_var->VendorGuid,
|
||||
new_var->Attributes,
|
||||
new_var->DataSize,
|
||||
new_var->Data);
|
||||
spin_lock(&efivars->lock);
|
||||
status = efivars->ops->set_variable(new_var->VariableName,
|
||||
&new_var->VendorGuid,
|
||||
new_var->Attributes,
|
||||
new_var->DataSize,
|
||||
new_var->Data);
|
||||
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
@ -319,7 +311,7 @@ efivar_show_raw(struct efivar_entry *entry, char *buf)
|
||||
if (!entry || !buf)
|
||||
return 0;
|
||||
|
||||
status = get_var_data(var);
|
||||
status = get_var_data(entry->efivars, var);
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EIO;
|
||||
|
||||
@ -407,6 +399,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
struct efi_variable *new_var = (struct efi_variable *)buf;
|
||||
struct efivars *efivars = bin_attr->private;
|
||||
struct efivar_entry *search_efivar, *n;
|
||||
unsigned long strsize1, strsize2;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
@ -415,12 +408,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock(&efivars_lock);
|
||||
spin_lock(&efivars->lock);
|
||||
|
||||
/*
|
||||
* Does this variable already exist?
|
||||
*/
|
||||
list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
|
||||
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
|
||||
strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
|
||||
strsize2 = utf8_strsize(new_var->VariableName, 1024);
|
||||
if (strsize1 == strsize2 &&
|
||||
@ -433,28 +426,31 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* now *really* create the variable via EFI */
|
||||
status = efi.set_variable(new_var->VariableName,
|
||||
&new_var->VendorGuid,
|
||||
new_var->Attributes,
|
||||
new_var->DataSize,
|
||||
new_var->Data);
|
||||
status = efivars->ops->set_variable(new_var->VariableName,
|
||||
&new_var->VendorGuid,
|
||||
new_var->Attributes,
|
||||
new_var->DataSize,
|
||||
new_var->Data);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
status);
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
return -EIO;
|
||||
}
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
|
||||
/* Create the entry in sysfs. Locking is not required here */
|
||||
status = efivar_create_sysfs_entry(utf8_strsize(new_var->VariableName,
|
||||
1024), new_var->VariableName, &new_var->VendorGuid);
|
||||
status = efivar_create_sysfs_entry(efivars,
|
||||
utf8_strsize(new_var->VariableName,
|
||||
1024),
|
||||
new_var->VariableName,
|
||||
&new_var->VendorGuid);
|
||||
if (status) {
|
||||
printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n");
|
||||
}
|
||||
@ -466,6 +462,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
char *buf, loff_t pos, size_t count)
|
||||
{
|
||||
struct efi_variable *del_var = (struct efi_variable *)buf;
|
||||
struct efivars *efivars = bin_attr->private;
|
||||
struct efivar_entry *search_efivar, *n;
|
||||
unsigned long strsize1, strsize2;
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
@ -474,12 +471,12 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
spin_lock(&efivars_lock);
|
||||
spin_lock(&efivars->lock);
|
||||
|
||||
/*
|
||||
* Does this variable already exist?
|
||||
*/
|
||||
list_for_each_entry_safe(search_efivar, n, &efivar_list, list) {
|
||||
list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
|
||||
strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
|
||||
strsize2 = utf8_strsize(del_var->VariableName, 1024);
|
||||
if (strsize1 == strsize2 &&
|
||||
@ -492,44 +489,34 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* force the Attributes/DataSize to 0 to ensure deletion */
|
||||
del_var->Attributes = 0;
|
||||
del_var->DataSize = 0;
|
||||
|
||||
status = efi.set_variable(del_var->VariableName,
|
||||
&del_var->VendorGuid,
|
||||
del_var->Attributes,
|
||||
del_var->DataSize,
|
||||
del_var->Data);
|
||||
status = efivars->ops->set_variable(del_var->VariableName,
|
||||
&del_var->VendorGuid,
|
||||
del_var->Attributes,
|
||||
del_var->DataSize,
|
||||
del_var->Data);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n",
|
||||
status);
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
return -EIO;
|
||||
}
|
||||
list_del(&search_efivar->list);
|
||||
/* We need to release this lock before unregistering. */
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_unlock(&efivars->lock);
|
||||
efivar_unregister(search_efivar);
|
||||
|
||||
/* It's dead Jim.... */
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct bin_attribute var_subsys_attr_new_var = {
|
||||
.attr = {.name = "new_var", .mode = 0200},
|
||||
.write = efivar_create,
|
||||
};
|
||||
|
||||
static struct bin_attribute var_subsys_attr_del_var = {
|
||||
.attr = {.name = "del_var", .mode = 0200},
|
||||
.write = efivar_delete,
|
||||
};
|
||||
|
||||
/*
|
||||
* Let's not leave out systab information that snuck into
|
||||
* the efivars driver
|
||||
@ -572,8 +559,6 @@ static struct attribute_group efi_subsys_attr_group = {
|
||||
.attrs = efi_subsys_attrs,
|
||||
};
|
||||
|
||||
|
||||
static struct kset *vars_kset;
|
||||
static struct kobject *efi_kobj;
|
||||
|
||||
/*
|
||||
@ -582,13 +567,14 @@ static struct kobject *efi_kobj;
|
||||
* variable_name_size = number of bytes required to hold
|
||||
* variable_name (not counting the NULL
|
||||
* character at the end.
|
||||
* efivars_lock is not held on entry or exit.
|
||||
* efivars->lock is not held on entry or exit.
|
||||
* Returns 1 on failure, 0 on success
|
||||
*/
|
||||
static int
|
||||
efivar_create_sysfs_entry(unsigned long variable_name_size,
|
||||
efi_char16_t *variable_name,
|
||||
efi_guid_t *vendor_guid)
|
||||
efivar_create_sysfs_entry(struct efivars *efivars,
|
||||
unsigned long variable_name_size,
|
||||
efi_char16_t *variable_name,
|
||||
efi_guid_t *vendor_guid)
|
||||
{
|
||||
int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
|
||||
char *short_name;
|
||||
@ -603,6 +589,7 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
|
||||
return 1;
|
||||
}
|
||||
|
||||
new_efivar->efivars = efivars;
|
||||
memcpy(new_efivar->var.VariableName, variable_name,
|
||||
variable_name_size);
|
||||
memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t));
|
||||
@ -618,7 +605,7 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
|
||||
*(short_name + strlen(short_name)) = '-';
|
||||
efi_guid_unparse(vendor_guid, short_name + strlen(short_name));
|
||||
|
||||
new_efivar->kobj.kset = vars_kset;
|
||||
new_efivar->kobj.kset = efivars->kset;
|
||||
i = kobject_init_and_add(&new_efivar->kobj, &efivar_ktype, NULL,
|
||||
"%s", short_name);
|
||||
if (i) {
|
||||
@ -631,22 +618,95 @@ efivar_create_sysfs_entry(unsigned long variable_name_size,
|
||||
kfree(short_name);
|
||||
short_name = NULL;
|
||||
|
||||
spin_lock(&efivars_lock);
|
||||
list_add(&new_efivar->list, &efivar_list);
|
||||
spin_unlock(&efivars_lock);
|
||||
spin_lock(&efivars->lock);
|
||||
list_add(&new_efivar->list, &efivars->list);
|
||||
spin_unlock(&efivars->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* For now we register the efi subsystem with the firmware subsystem
|
||||
* and the vars subsystem with the efi subsystem. In the future, it
|
||||
* might make sense to split off the efi subsystem into its own
|
||||
* driver, but for now only efivars will register with it, so just
|
||||
* include it here.
|
||||
*/
|
||||
|
||||
static int __init
|
||||
efivars_init(void)
|
||||
static int
|
||||
create_efivars_bin_attributes(struct efivars *efivars)
|
||||
{
|
||||
struct bin_attribute *attr;
|
||||
int error;
|
||||
|
||||
/* new_var */
|
||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
||||
if (!attr)
|
||||
return -ENOMEM;
|
||||
|
||||
attr->attr.name = "new_var";
|
||||
attr->attr.mode = 0200;
|
||||
attr->write = efivar_create;
|
||||
attr->private = efivars;
|
||||
efivars->new_var = attr;
|
||||
|
||||
/* del_var */
|
||||
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
|
||||
if (!attr) {
|
||||
error = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
attr->attr.name = "del_var";
|
||||
attr->attr.mode = 0200;
|
||||
attr->write = efivar_delete;
|
||||
attr->private = efivars;
|
||||
efivars->del_var = attr;
|
||||
|
||||
sysfs_bin_attr_init(efivars->new_var);
|
||||
sysfs_bin_attr_init(efivars->del_var);
|
||||
|
||||
/* Register */
|
||||
error = sysfs_create_bin_file(&efivars->kset->kobj,
|
||||
efivars->new_var);
|
||||
if (error) {
|
||||
printk(KERN_ERR "efivars: unable to create new_var sysfs file"
|
||||
" due to error %d\n", error);
|
||||
goto out_free;
|
||||
}
|
||||
error = sysfs_create_bin_file(&efivars->kset->kobj,
|
||||
efivars->del_var);
|
||||
if (error) {
|
||||
printk(KERN_ERR "efivars: unable to create del_var sysfs file"
|
||||
" due to error %d\n", error);
|
||||
sysfs_remove_bin_file(&efivars->kset->kobj,
|
||||
efivars->new_var);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_free:
|
||||
kfree(efivars->new_var);
|
||||
efivars->new_var = NULL;
|
||||
kfree(efivars->new_var);
|
||||
efivars->new_var = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
void unregister_efivars(struct efivars *efivars)
|
||||
{
|
||||
struct efivar_entry *entry, *n;
|
||||
|
||||
list_for_each_entry_safe(entry, n, &efivars->list, list) {
|
||||
spin_lock(&efivars->lock);
|
||||
list_del(&entry->list);
|
||||
spin_unlock(&efivars->lock);
|
||||
efivar_unregister(entry);
|
||||
}
|
||||
if (efivars->new_var)
|
||||
sysfs_remove_bin_file(&efivars->kset->kobj, efivars->new_var);
|
||||
if (efivars->del_var)
|
||||
sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var);
|
||||
kfree(efivars->new_var);
|
||||
kfree(efivars->del_var);
|
||||
kset_unregister(efivars->kset);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_efivars);
|
||||
|
||||
int register_efivars(struct efivars *efivars,
|
||||
const struct efivar_operations *ops,
|
||||
struct kobject *parent_kobj)
|
||||
{
|
||||
efi_status_t status = EFI_NOT_FOUND;
|
||||
efi_guid_t vendor_guid;
|
||||
@ -654,31 +714,21 @@ efivars_init(void)
|
||||
unsigned long variable_name_size = 1024;
|
||||
int error = 0;
|
||||
|
||||
if (!efi_enabled)
|
||||
return -ENODEV;
|
||||
|
||||
variable_name = kzalloc(variable_name_size, GFP_KERNEL);
|
||||
if (!variable_name) {
|
||||
printk(KERN_ERR "efivars: Memory allocation failed.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
|
||||
EFIVARS_DATE);
|
||||
spin_lock_init(&efivars->lock);
|
||||
INIT_LIST_HEAD(&efivars->list);
|
||||
efivars->ops = ops;
|
||||
|
||||
/* For now we'll register the efi directory at /sys/firmware/efi */
|
||||
efi_kobj = kobject_create_and_add("efi", firmware_kobj);
|
||||
if (!efi_kobj) {
|
||||
printk(KERN_ERR "efivars: Firmware registration failed.\n");
|
||||
error = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
vars_kset = kset_create_and_add("vars", NULL, efi_kobj);
|
||||
if (!vars_kset) {
|
||||
efivars->kset = kset_create_and_add("vars", NULL, parent_kobj);
|
||||
if (!efivars->kset) {
|
||||
printk(KERN_ERR "efivars: Subsystem registration failed.\n");
|
||||
error = -ENOMEM;
|
||||
goto out_firmware_unregister;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -689,14 +739,15 @@ efivars_init(void)
|
||||
do {
|
||||
variable_name_size = 1024;
|
||||
|
||||
status = efi.get_next_variable(&variable_name_size,
|
||||
status = ops->get_next_variable(&variable_name_size,
|
||||
variable_name,
|
||||
&vendor_guid);
|
||||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
efivar_create_sysfs_entry(variable_name_size,
|
||||
variable_name,
|
||||
&vendor_guid);
|
||||
efivar_create_sysfs_entry(efivars,
|
||||
variable_name_size,
|
||||
variable_name,
|
||||
&vendor_guid);
|
||||
break;
|
||||
case EFI_NOT_FOUND:
|
||||
break;
|
||||
@ -708,35 +759,60 @@ efivars_init(void)
|
||||
}
|
||||
} while (status != EFI_NOT_FOUND);
|
||||
|
||||
/*
|
||||
* Now add attributes to allow creation of new vars
|
||||
* and deletion of existing ones...
|
||||
*/
|
||||
error = sysfs_create_bin_file(&vars_kset->kobj,
|
||||
&var_subsys_attr_new_var);
|
||||
error = create_efivars_bin_attributes(efivars);
|
||||
if (error)
|
||||
printk(KERN_ERR "efivars: unable to create new_var sysfs file"
|
||||
" due to error %d\n", error);
|
||||
error = sysfs_create_bin_file(&vars_kset->kobj,
|
||||
&var_subsys_attr_del_var);
|
||||
if (error)
|
||||
printk(KERN_ERR "efivars: unable to create del_var sysfs file"
|
||||
" due to error %d\n", error);
|
||||
unregister_efivars(efivars);
|
||||
|
||||
out:
|
||||
kfree(variable_name);
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_efivars);
|
||||
|
||||
static struct efivars __efivars;
|
||||
static struct efivar_operations ops;
|
||||
|
||||
/*
|
||||
* For now we register the efi subsystem with the firmware subsystem
|
||||
* and the vars subsystem with the efi subsystem. In the future, it
|
||||
* might make sense to split off the efi subsystem into its own
|
||||
* driver, but for now only efivars will register with it, so just
|
||||
* include it here.
|
||||
*/
|
||||
|
||||
static int __init
|
||||
efivars_init(void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
|
||||
EFIVARS_DATE);
|
||||
|
||||
if (!efi_enabled)
|
||||
return 0;
|
||||
|
||||
/* For now we'll register the efi directory at /sys/firmware/efi */
|
||||
efi_kobj = kobject_create_and_add("efi", firmware_kobj);
|
||||
if (!efi_kobj) {
|
||||
printk(KERN_ERR "efivars: Firmware registration failed.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ops.get_variable = efi.get_variable;
|
||||
ops.set_variable = efi.set_variable;
|
||||
ops.get_next_variable = efi.get_next_variable;
|
||||
error = register_efivars(&__efivars, &ops, efi_kobj);
|
||||
|
||||
/* Don't forget the systab entry */
|
||||
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
|
||||
if (error)
|
||||
printk(KERN_ERR "efivars: Sysfs attribute export failed with error %d.\n", error);
|
||||
else
|
||||
goto out_free;
|
||||
|
||||
kset_unregister(vars_kset);
|
||||
|
||||
out_firmware_unregister:
|
||||
kobject_put(efi_kobj);
|
||||
|
||||
out_free:
|
||||
kfree(variable_name);
|
||||
if (error) {
|
||||
printk(KERN_ERR
|
||||
"efivars: Sysfs attribute export failed with error %d.\n",
|
||||
error);
|
||||
unregister_efivars(&__efivars);
|
||||
kobject_put(efi_kobj);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -744,16 +820,7 @@ out_free:
|
||||
static void __exit
|
||||
efivars_exit(void)
|
||||
{
|
||||
struct efivar_entry *entry, *n;
|
||||
|
||||
list_for_each_entry_safe(entry, n, &efivar_list, list) {
|
||||
spin_lock(&efivars_lock);
|
||||
list_del(&entry->list);
|
||||
spin_unlock(&efivars_lock);
|
||||
efivar_unregister(entry);
|
||||
}
|
||||
|
||||
kset_unregister(vars_kset);
|
||||
unregister_efivars(&__efivars);
|
||||
kobject_put(efi_kobj);
|
||||
}
|
||||
|
||||
|
@ -441,7 +441,7 @@ config BMP085
|
||||
module will be called bmp085.
|
||||
|
||||
config PCH_PHUB
|
||||
tristate "PCH Packet Hub of Intel Topcliff"
|
||||
tristate "PCH Packet Hub of Intel Topcliff / OKI SEMICONDUCTOR ML7213"
|
||||
depends on PCI
|
||||
help
|
||||
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
|
||||
@ -449,6 +449,11 @@ config PCH_PHUB
|
||||
processor. The Topcliff has MAC address and Option ROM data in SROM.
|
||||
This driver can access MAC address and Option ROM data in SROM.
|
||||
|
||||
This driver also can be used for OKI SEMICONDUCTOR's ML7213 which is
|
||||
for IVI(In-Vehicle Infotainment) use.
|
||||
ML7213 is companion chip for Intel Atom E6xx series.
|
||||
ML7213 is completely compatible for Intel EG20T PCH.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called pch_phub.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
|
||||
* Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -33,7 +33,12 @@
|
||||
#define PHUB_TIMEOUT 0x05 /* Time out value for Status Register */
|
||||
#define PCH_PHUB_ROM_WRITE_ENABLE 0x01 /* Enabling for writing ROM */
|
||||
#define PCH_PHUB_ROM_WRITE_DISABLE 0x00 /* Disabling for writing ROM */
|
||||
#define PCH_PHUB_ROM_START_ADDR 0x14 /* ROM data area start address offset */
|
||||
#define PCH_PHUB_MAC_START_ADDR 0x20C /* MAC data area start address offset */
|
||||
#define PCH_PHUB_ROM_START_ADDR_EG20T 0x14 /* ROM data area start address offset
|
||||
(Intel EG20T PCH)*/
|
||||
#define PCH_PHUB_ROM_START_ADDR_ML7213 0x400 /* ROM data area start address
|
||||
offset(OKI SEMICONDUCTOR ML7213)
|
||||
*/
|
||||
|
||||
/* MAX number of INT_REDUCE_CONTROL registers */
|
||||
#define MAX_NUM_INT_REDUCE_CONTROL_REG 128
|
||||
@ -42,6 +47,10 @@
|
||||
#define CLKCFG_CAN_50MHZ 0x12000000
|
||||
#define CLKCFG_CANCLK_MASK 0xFF000000
|
||||
|
||||
/* Macros for ML7213 */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10db
|
||||
#define PCI_DEVICE_ID_ROHM_ML7213_PHUB 0x801A
|
||||
|
||||
/* SROM ACCESS Macro */
|
||||
#define PCH_WORD_ADDR_MASK (~((1 << 2) - 1))
|
||||
|
||||
@ -298,7 +307,7 @@ static void pch_phub_read_serial_rom_val(struct pch_phub_reg *chip,
|
||||
{
|
||||
unsigned int mem_addr;
|
||||
|
||||
mem_addr = PCH_PHUB_ROM_START_ADDR +
|
||||
mem_addr = PCH_PHUB_ROM_START_ADDR_EG20T +
|
||||
pch_phub_mac_offset[offset_address];
|
||||
|
||||
pch_phub_read_serial_rom(chip, mem_addr, data);
|
||||
@ -315,7 +324,7 @@ static int pch_phub_write_serial_rom_val(struct pch_phub_reg *chip,
|
||||
int retval;
|
||||
unsigned int mem_addr;
|
||||
|
||||
mem_addr = PCH_PHUB_ROM_START_ADDR +
|
||||
mem_addr = PCH_PHUB_ROM_START_ADDR_EG20T +
|
||||
pch_phub_mac_offset[offset_address];
|
||||
|
||||
retval = pch_phub_write_serial_rom(chip, mem_addr, data);
|
||||
@ -594,24 +603,39 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
|
||||
"pch_phub_extrom_base_address variable is %p\n", __func__,
|
||||
chip->pch_phub_extrom_base_address);
|
||||
|
||||
if (id->driver_data == 1) {
|
||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
goto err_sysfs_create;
|
||||
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
goto exit_bin_attr;
|
||||
|
||||
pch_phub_read_modify_write_reg(chip,
|
||||
(unsigned int)CLKCFG_REG_OFFSET,
|
||||
CLKCFG_CAN_50MHZ,
|
||||
CLKCFG_CANCLK_MASK);
|
||||
|
||||
/* set the prefech value */
|
||||
iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14);
|
||||
/* set the interrupt delay value */
|
||||
iowrite32(0x25, chip->pch_phub_base_address + 0x44);
|
||||
} else if (id->driver_data == 2) {
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
goto err_sysfs_create;
|
||||
/* set the prefech value
|
||||
* Device2(USB OHCI #1/ USB EHCI #1/ USB Device):a
|
||||
* Device4(SDIO #0,1,2):f
|
||||
* Device6(SATA 2):f
|
||||
* Device8(USB OHCI #0/ USB EHCI #0):a
|
||||
*/
|
||||
iowrite32(0x000affa0, chip->pch_phub_base_address + 0x14);
|
||||
}
|
||||
pci_set_drvdata(pdev, chip);
|
||||
|
||||
retval = sysfs_create_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
goto err_sysfs_create;
|
||||
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
|
||||
if (retval)
|
||||
goto exit_bin_attr;
|
||||
|
||||
pch_phub_read_modify_write_reg(chip, (unsigned int)CLKCFG_REG_OFFSET,
|
||||
CLKCFG_CAN_50MHZ, CLKCFG_CANCLK_MASK);
|
||||
|
||||
/* set the prefech value */
|
||||
iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14);
|
||||
/* set the interrupt delay value */
|
||||
iowrite32(0x25, chip->pch_phub_base_address + 0x44);
|
||||
|
||||
return 0;
|
||||
exit_bin_attr:
|
||||
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_pch_mac.attr);
|
||||
@ -687,8 +711,9 @@ static int pch_phub_resume(struct pci_dev *pdev)
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct pci_device_id pch_phub_pcidev_id[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH1_PHUB)},
|
||||
{0,}
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), 1, },
|
||||
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct pci_driver pch_phub_driver = {
|
||||
|
@ -25,10 +25,9 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
/* understand BT, FM and GPS for now */
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/ti_wilink_st.h>
|
||||
|
||||
/* function pointer pointing to either,
|
||||
@ -38,21 +37,38 @@
|
||||
void (*st_recv) (void*, const unsigned char*, long);
|
||||
|
||||
/********************************************************************/
|
||||
#if 0
|
||||
/* internal misc functions */
|
||||
bool is_protocol_list_empty(void)
|
||||
static void add_channel_to_table(struct st_data_s *st_gdata,
|
||||
struct st_proto_s *new_proto)
|
||||
{
|
||||
unsigned char i = 0;
|
||||
pr_debug(" %s ", __func__);
|
||||
for (i = 0; i < ST_MAX; i++) {
|
||||
if (st_gdata->list[i] != NULL)
|
||||
return ST_NOTEMPTY;
|
||||
/* not empty */
|
||||
}
|
||||
/* list empty */
|
||||
return ST_EMPTY;
|
||||
pr_info("%s: id %d\n", __func__, new_proto->chnl_id);
|
||||
/* list now has the channel id as index itself */
|
||||
st_gdata->list[new_proto->chnl_id] = new_proto;
|
||||
}
|
||||
|
||||
static void remove_channel_from_table(struct st_data_s *st_gdata,
|
||||
struct st_proto_s *proto)
|
||||
{
|
||||
pr_info("%s: id %d\n", __func__, proto->chnl_id);
|
||||
st_gdata->list[proto->chnl_id] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* called from KIM during firmware download.
|
||||
*
|
||||
* This is a wrapper function to tty->ops->write_room.
|
||||
* It returns number of free space available in
|
||||
* uart tx buffer.
|
||||
*/
|
||||
int st_get_uart_wr_room(struct st_data_s *st_gdata)
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) {
|
||||
pr_err("tty unavailable to perform write");
|
||||
return -1;
|
||||
}
|
||||
tty = st_gdata->tty;
|
||||
return tty->ops->write_room(tty);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* can be called in from
|
||||
* -- KIM (during fw download)
|
||||
@ -67,7 +83,7 @@ int st_int_write(struct st_data_s *st_gdata,
|
||||
struct tty_struct *tty;
|
||||
if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) {
|
||||
pr_err("tty unavailable to perform write");
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
tty = st_gdata->tty;
|
||||
#ifdef VERBOSE
|
||||
@ -82,15 +98,15 @@ int st_int_write(struct st_data_s *st_gdata,
|
||||
* push the skb received to relevant
|
||||
* protocol stacks
|
||||
*/
|
||||
void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)
|
||||
void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
|
||||
{
|
||||
pr_info(" %s(prot:%d) ", __func__, protoid);
|
||||
pr_debug(" %s(prot:%d) ", __func__, chnl_id);
|
||||
|
||||
if (unlikely
|
||||
(st_gdata == NULL || st_gdata->rx_skb == NULL
|
||||
|| st_gdata->list[protoid] == NULL)) {
|
||||
pr_err("protocol %d not registered, no data to send?",
|
||||
protoid);
|
||||
|| st_gdata->list[chnl_id] == NULL)) {
|
||||
pr_err("chnl_id %d not registered, no data to send?",
|
||||
chnl_id);
|
||||
kfree_skb(st_gdata->rx_skb);
|
||||
return;
|
||||
}
|
||||
@ -99,17 +115,17 @@ void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)
|
||||
* - should be just skb_queue_tail for the
|
||||
* protocol stack driver
|
||||
*/
|
||||
if (likely(st_gdata->list[protoid]->recv != NULL)) {
|
||||
if (likely(st_gdata->list[chnl_id]->recv != NULL)) {
|
||||
if (unlikely
|
||||
(st_gdata->list[protoid]->recv
|
||||
(st_gdata->list[protoid]->priv_data, st_gdata->rx_skb)
|
||||
(st_gdata->list[chnl_id]->recv
|
||||
(st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb)
|
||||
!= 0)) {
|
||||
pr_err(" proto stack %d's ->recv failed", protoid);
|
||||
pr_err(" proto stack %d's ->recv failed", chnl_id);
|
||||
kfree_skb(st_gdata->rx_skb);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
pr_err(" proto stack %d's ->recv null", protoid);
|
||||
pr_err(" proto stack %d's ->recv null", chnl_id);
|
||||
kfree_skb(st_gdata->rx_skb);
|
||||
}
|
||||
return;
|
||||
@ -124,16 +140,22 @@ void st_reg_complete(struct st_data_s *st_gdata, char err)
|
||||
{
|
||||
unsigned char i = 0;
|
||||
pr_info(" %s ", __func__);
|
||||
for (i = 0; i < ST_MAX; i++) {
|
||||
for (i = 0; i < ST_MAX_CHANNELS; i++) {
|
||||
if (likely(st_gdata != NULL && st_gdata->list[i] != NULL &&
|
||||
st_gdata->list[i]->reg_complete_cb != NULL))
|
||||
st_gdata->list[i]->reg_complete_cb != NULL)) {
|
||||
st_gdata->list[i]->reg_complete_cb
|
||||
(st_gdata->list[i]->priv_data, err);
|
||||
pr_info("protocol %d's cb sent %d\n", i, err);
|
||||
if (err) { /* cleanup registered protocol */
|
||||
st_gdata->protos_registered--;
|
||||
st_gdata->list[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int st_check_data_len(struct st_data_s *st_gdata,
|
||||
int protoid, int len)
|
||||
unsigned char chnl_id, int len)
|
||||
{
|
||||
int room = skb_tailroom(st_gdata->rx_skb);
|
||||
|
||||
@ -144,7 +166,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
|
||||
* has zero length payload. So, ask ST CORE to
|
||||
* forward the packet to protocol driver (BT/FM/GPS)
|
||||
*/
|
||||
st_send_frame(protoid, st_gdata);
|
||||
st_send_frame(chnl_id, st_gdata);
|
||||
|
||||
} else if (len > room) {
|
||||
/* Received packet's payload length is larger.
|
||||
@ -157,7 +179,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
|
||||
/* Packet header has non-zero payload length and
|
||||
* we have enough space in created skb. Lets read
|
||||
* payload data */
|
||||
st_gdata->rx_state = ST_BT_W4_DATA;
|
||||
st_gdata->rx_state = ST_W4_DATA;
|
||||
st_gdata->rx_count = len;
|
||||
return len;
|
||||
}
|
||||
@ -167,6 +189,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,
|
||||
st_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
st_gdata->rx_skb = NULL;
|
||||
st_gdata->rx_count = 0;
|
||||
st_gdata->rx_chnl = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -208,14 +231,12 @@ void st_int_recv(void *disc_data,
|
||||
const unsigned char *data, long count)
|
||||
{
|
||||
char *ptr;
|
||||
struct hci_event_hdr *eh;
|
||||
struct hci_acl_hdr *ah;
|
||||
struct hci_sco_hdr *sh;
|
||||
struct fm_event_hdr *fm;
|
||||
struct gps_event_hdr *gps;
|
||||
int len = 0, type = 0, dlen = 0;
|
||||
static enum proto_type protoid = ST_MAX;
|
||||
struct st_proto_s *proto;
|
||||
unsigned short payload_len = 0;
|
||||
int len = 0, type = 0;
|
||||
unsigned char *plen;
|
||||
struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
|
||||
unsigned long flags;
|
||||
|
||||
ptr = (char *)data;
|
||||
/* tty_receive sent null ? */
|
||||
@ -224,10 +245,11 @@ void st_int_recv(void *disc_data,
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("count %ld rx_state %ld"
|
||||
pr_debug("count %ld rx_state %ld"
|
||||
"rx_count %ld", count, st_gdata->rx_state,
|
||||
st_gdata->rx_count);
|
||||
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
/* Decode received bytes here */
|
||||
while (count) {
|
||||
if (st_gdata->rx_count) {
|
||||
@ -242,64 +264,36 @@ void st_int_recv(void *disc_data,
|
||||
|
||||
/* Check ST RX state machine , where are we? */
|
||||
switch (st_gdata->rx_state) {
|
||||
|
||||
/* Waiting for complete packet ? */
|
||||
case ST_BT_W4_DATA:
|
||||
/* Waiting for complete packet ? */
|
||||
case ST_W4_DATA:
|
||||
pr_debug("Complete pkt received");
|
||||
|
||||
/* Ask ST CORE to forward
|
||||
* the packet to protocol driver */
|
||||
st_send_frame(protoid, st_gdata);
|
||||
st_send_frame(st_gdata->rx_chnl, st_gdata);
|
||||
|
||||
st_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
st_gdata->rx_skb = NULL;
|
||||
protoid = ST_MAX; /* is this required ? */
|
||||
continue;
|
||||
|
||||
/* Waiting for Bluetooth event header ? */
|
||||
case ST_BT_W4_EVENT_HDR:
|
||||
eh = (struct hci_event_hdr *)st_gdata->rx_skb->
|
||||
data;
|
||||
|
||||
pr_debug("Event header: evt 0x%2.2x"
|
||||
"plen %d", eh->evt, eh->plen);
|
||||
|
||||
st_check_data_len(st_gdata, protoid, eh->plen);
|
||||
continue;
|
||||
|
||||
/* Waiting for Bluetooth acl header ? */
|
||||
case ST_BT_W4_ACL_HDR:
|
||||
ah = (struct hci_acl_hdr *)st_gdata->rx_skb->
|
||||
data;
|
||||
dlen = __le16_to_cpu(ah->dlen);
|
||||
|
||||
pr_info("ACL header: dlen %d", dlen);
|
||||
|
||||
st_check_data_len(st_gdata, protoid, dlen);
|
||||
continue;
|
||||
|
||||
/* Waiting for Bluetooth sco header ? */
|
||||
case ST_BT_W4_SCO_HDR:
|
||||
sh = (struct hci_sco_hdr *)st_gdata->rx_skb->
|
||||
data;
|
||||
|
||||
pr_info("SCO header: dlen %d", sh->dlen);
|
||||
|
||||
st_check_data_len(st_gdata, protoid, sh->dlen);
|
||||
continue;
|
||||
case ST_FM_W4_EVENT_HDR:
|
||||
fm = (struct fm_event_hdr *)st_gdata->rx_skb->
|
||||
data;
|
||||
pr_info("FM Header: ");
|
||||
st_check_data_len(st_gdata, ST_FM, fm->plen);
|
||||
continue;
|
||||
/* TODO : Add GPS packet machine logic here */
|
||||
case ST_GPS_W4_EVENT_HDR:
|
||||
/* [0x09 pkt hdr][R/W byte][2 byte len] */
|
||||
gps = (struct gps_event_hdr *)st_gdata->rx_skb->
|
||||
data;
|
||||
pr_info("GPS Header: ");
|
||||
st_check_data_len(st_gdata, ST_GPS, gps->plen);
|
||||
/* parse the header to know details */
|
||||
case ST_W4_HEADER:
|
||||
proto = st_gdata->list[st_gdata->rx_chnl];
|
||||
plen =
|
||||
&st_gdata->rx_skb->data
|
||||
[proto->offset_len_in_hdr];
|
||||
pr_debug("plen pointing to %x\n", *plen);
|
||||
if (proto->len_size == 1)/* 1 byte len field */
|
||||
payload_len = *(unsigned char *)plen;
|
||||
else if (proto->len_size == 2)
|
||||
payload_len =
|
||||
__le16_to_cpu(*(unsigned short *)plen);
|
||||
else
|
||||
pr_info("%s: invalid length "
|
||||
"for id %d\n",
|
||||
__func__, proto->chnl_id);
|
||||
st_check_data_len(st_gdata, proto->chnl_id,
|
||||
payload_len);
|
||||
pr_debug("off %d, pay len %d\n",
|
||||
proto->offset_len_in_hdr, payload_len);
|
||||
continue;
|
||||
} /* end of switch rx_state */
|
||||
}
|
||||
@ -308,123 +302,56 @@ void st_int_recv(void *disc_data,
|
||||
/* Check first byte of packet and identify module
|
||||
* owner (BT/FM/GPS) */
|
||||
switch (*ptr) {
|
||||
|
||||
/* Bluetooth event packet? */
|
||||
case HCI_EVENT_PKT:
|
||||
pr_info("Event packet");
|
||||
st_gdata->rx_state = ST_BT_W4_EVENT_HDR;
|
||||
st_gdata->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
type = HCI_EVENT_PKT;
|
||||
protoid = ST_BT;
|
||||
break;
|
||||
|
||||
/* Bluetooth acl packet? */
|
||||
case HCI_ACLDATA_PKT:
|
||||
pr_info("ACL packet");
|
||||
st_gdata->rx_state = ST_BT_W4_ACL_HDR;
|
||||
st_gdata->rx_count = HCI_ACL_HDR_SIZE;
|
||||
type = HCI_ACLDATA_PKT;
|
||||
protoid = ST_BT;
|
||||
break;
|
||||
|
||||
/* Bluetooth sco packet? */
|
||||
case HCI_SCODATA_PKT:
|
||||
pr_info("SCO packet");
|
||||
st_gdata->rx_state = ST_BT_W4_SCO_HDR;
|
||||
st_gdata->rx_count = HCI_SCO_HDR_SIZE;
|
||||
type = HCI_SCODATA_PKT;
|
||||
protoid = ST_BT;
|
||||
break;
|
||||
|
||||
/* Channel 8(FM) packet? */
|
||||
case ST_FM_CH8_PKT:
|
||||
pr_info("FM CH8 packet");
|
||||
type = ST_FM_CH8_PKT;
|
||||
st_gdata->rx_state = ST_FM_W4_EVENT_HDR;
|
||||
st_gdata->rx_count = FM_EVENT_HDR_SIZE;
|
||||
protoid = ST_FM;
|
||||
break;
|
||||
|
||||
/* Channel 9(GPS) packet? */
|
||||
case 0x9: /*ST_LL_GPS_CH9_PKT */
|
||||
pr_info("GPS CH9 packet");
|
||||
type = 0x9; /* ST_LL_GPS_CH9_PKT; */
|
||||
protoid = ST_GPS;
|
||||
st_gdata->rx_state = ST_GPS_W4_EVENT_HDR;
|
||||
st_gdata->rx_count = 3; /* GPS_EVENT_HDR_SIZE -1*/
|
||||
break;
|
||||
case LL_SLEEP_IND:
|
||||
case LL_SLEEP_ACK:
|
||||
case LL_WAKE_UP_IND:
|
||||
pr_info("PM packet");
|
||||
pr_debug("PM packet");
|
||||
/* this takes appropriate action based on
|
||||
* sleep state received --
|
||||
*/
|
||||
st_ll_sleep_state(st_gdata, *ptr);
|
||||
/* if WAKEUP_IND collides copy from waitq to txq
|
||||
* and assume chip awake
|
||||
*/
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
if (st_ll_getstate(st_gdata) == ST_LL_AWAKE)
|
||||
st_wakeup_ack(st_gdata, LL_WAKE_UP_ACK);
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
|
||||
ptr++;
|
||||
count--;
|
||||
continue;
|
||||
case LL_WAKE_UP_ACK:
|
||||
pr_info("PM packet");
|
||||
pr_debug("PM packet");
|
||||
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
/* wake up ack received */
|
||||
st_wakeup_ack(st_gdata, *ptr);
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
|
||||
ptr++;
|
||||
count--;
|
||||
continue;
|
||||
/* Unknow packet? */
|
||||
default:
|
||||
pr_err("Unknown packet type %2.2x", (__u8) *ptr);
|
||||
ptr++;
|
||||
count--;
|
||||
continue;
|
||||
type = *ptr;
|
||||
st_gdata->rx_skb = alloc_skb(
|
||||
st_gdata->list[type]->max_frame_size,
|
||||
GFP_ATOMIC);
|
||||
skb_reserve(st_gdata->rx_skb,
|
||||
st_gdata->list[type]->reserve);
|
||||
/* next 2 required for BT only */
|
||||
st_gdata->rx_skb->cb[0] = type; /*pkt_type*/
|
||||
st_gdata->rx_skb->cb[1] = 0; /*incoming*/
|
||||
st_gdata->rx_chnl = *ptr;
|
||||
st_gdata->rx_state = ST_W4_HEADER;
|
||||
st_gdata->rx_count = st_gdata->list[type]->hdr_len;
|
||||
pr_debug("rx_count %ld\n", st_gdata->rx_count);
|
||||
};
|
||||
ptr++;
|
||||
count--;
|
||||
|
||||
switch (protoid) {
|
||||
case ST_BT:
|
||||
/* Allocate new packet to hold received data */
|
||||
st_gdata->rx_skb =
|
||||
bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!st_gdata->rx_skb) {
|
||||
pr_err("Can't allocate mem for new packet");
|
||||
st_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
st_gdata->rx_count = 0;
|
||||
return;
|
||||
}
|
||||
bt_cb(st_gdata->rx_skb)->pkt_type = type;
|
||||
break;
|
||||
case ST_FM: /* for FM */
|
||||
st_gdata->rx_skb =
|
||||
alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!st_gdata->rx_skb) {
|
||||
pr_err("Can't allocate mem for new packet");
|
||||
st_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
st_gdata->rx_count = 0;
|
||||
return;
|
||||
}
|
||||
/* place holder 0x08 */
|
||||
skb_reserve(st_gdata->rx_skb, 1);
|
||||
st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT;
|
||||
break;
|
||||
case ST_GPS:
|
||||
/* for GPS */
|
||||
st_gdata->rx_skb =
|
||||
alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , GFP_ATOMIC);
|
||||
if (!st_gdata->rx_skb) {
|
||||
pr_err("Can't allocate mem for new packet");
|
||||
st_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
st_gdata->rx_count = 0;
|
||||
return;
|
||||
}
|
||||
/* place holder 0x09 */
|
||||
skb_reserve(st_gdata->rx_skb, 1);
|
||||
st_gdata->rx_skb->cb[0] = 0x09; /*ST_GPS_CH9_PKT; */
|
||||
break;
|
||||
case ST_MAX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
pr_debug("done %s", __func__);
|
||||
return;
|
||||
}
|
||||
@ -466,7 +393,7 @@ void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
|
||||
|
||||
switch (st_ll_getstate(st_gdata)) {
|
||||
case ST_LL_AWAKE:
|
||||
pr_info("ST LL is AWAKE, sending normally");
|
||||
pr_debug("ST LL is AWAKE, sending normally");
|
||||
skb_queue_tail(&st_gdata->txq, skb);
|
||||
break;
|
||||
case ST_LL_ASLEEP_TO_AWAKE:
|
||||
@ -506,7 +433,7 @@ void st_tx_wakeup(struct st_data_s *st_data)
|
||||
pr_debug("%s", __func__);
|
||||
/* check for sending & set flag sending here */
|
||||
if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) {
|
||||
pr_info("ST already sending");
|
||||
pr_debug("ST already sending");
|
||||
/* keep sending */
|
||||
set_bit(ST_TX_WAKEUP, &st_data->tx_state);
|
||||
return;
|
||||
@ -548,9 +475,9 @@ void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf)
|
||||
{
|
||||
seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n",
|
||||
st_gdata->protos_registered,
|
||||
st_gdata->list[ST_BT] != NULL ? 'R' : 'U',
|
||||
st_gdata->list[ST_FM] != NULL ? 'R' : 'U',
|
||||
st_gdata->list[ST_GPS] != NULL ? 'R' : 'U');
|
||||
st_gdata->list[0x04] != NULL ? 'R' : 'U',
|
||||
st_gdata->list[0x08] != NULL ? 'R' : 'U',
|
||||
st_gdata->list[0x09] != NULL ? 'R' : 'U');
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
@ -565,20 +492,20 @@ long st_register(struct st_proto_s *new_proto)
|
||||
unsigned long flags = 0;
|
||||
|
||||
st_kim_ref(&st_gdata, 0);
|
||||
pr_info("%s(%d) ", __func__, new_proto->type);
|
||||
pr_info("%s(%d) ", __func__, new_proto->chnl_id);
|
||||
if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL
|
||||
|| new_proto->reg_complete_cb == NULL) {
|
||||
pr_err("gdata/new_proto/recv or reg_complete_cb not ready");
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_proto->type < ST_BT || new_proto->type >= ST_MAX) {
|
||||
pr_err("protocol %d not supported", new_proto->type);
|
||||
if (new_proto->chnl_id >= ST_MAX_CHANNELS) {
|
||||
pr_err("chnl_id %d not supported", new_proto->chnl_id);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
if (st_gdata->list[new_proto->type] != NULL) {
|
||||
pr_err("protocol %d already registered", new_proto->type);
|
||||
if (st_gdata->list[new_proto->chnl_id] != NULL) {
|
||||
pr_err("chnl_id %d already registered", new_proto->chnl_id);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
@ -586,11 +513,10 @@ long st_register(struct st_proto_s *new_proto)
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
|
||||
if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) {
|
||||
pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->type);
|
||||
pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id);
|
||||
/* fw download in progress */
|
||||
st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
|
||||
|
||||
st_gdata->list[new_proto->type] = new_proto;
|
||||
add_channel_to_table(st_gdata, new_proto);
|
||||
st_gdata->protos_registered++;
|
||||
new_proto->write = st_write;
|
||||
|
||||
@ -598,7 +524,7 @@ long st_register(struct st_proto_s *new_proto)
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
return -EINPROGRESS;
|
||||
} else if (st_gdata->protos_registered == ST_EMPTY) {
|
||||
pr_info(" protocol list empty :%d ", new_proto->type);
|
||||
pr_info(" chnl_id list empty :%d ", new_proto->chnl_id);
|
||||
set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
|
||||
st_recv = st_kim_recv;
|
||||
|
||||
@ -616,16 +542,11 @@ long st_register(struct st_proto_s *new_proto)
|
||||
if ((st_gdata->protos_registered != ST_EMPTY) &&
|
||||
(test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
|
||||
pr_err(" KIM failure complete callback ");
|
||||
st_reg_complete(st_gdata, -1);
|
||||
st_reg_complete(st_gdata, err);
|
||||
}
|
||||
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the protocol might require other gpios to be toggled
|
||||
*/
|
||||
st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
|
||||
|
||||
clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
|
||||
st_recv = st_int_recv;
|
||||
|
||||
@ -642,14 +563,14 @@ long st_register(struct st_proto_s *new_proto)
|
||||
/* check for already registered once more,
|
||||
* since the above check is old
|
||||
*/
|
||||
if (st_gdata->list[new_proto->type] != NULL) {
|
||||
if (st_gdata->list[new_proto->chnl_id] != NULL) {
|
||||
pr_err(" proto %d already registered ",
|
||||
new_proto->type);
|
||||
new_proto->chnl_id);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
st_gdata->list[new_proto->type] = new_proto;
|
||||
add_channel_to_table(st_gdata, new_proto);
|
||||
st_gdata->protos_registered++;
|
||||
new_proto->write = st_write;
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
@ -657,22 +578,7 @@ long st_register(struct st_proto_s *new_proto)
|
||||
}
|
||||
/* if fw is already downloaded & new stack registers protocol */
|
||||
else {
|
||||
switch (new_proto->type) {
|
||||
case ST_BT:
|
||||
/* do nothing */
|
||||
break;
|
||||
case ST_FM:
|
||||
case ST_GPS:
|
||||
st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
|
||||
break;
|
||||
case ST_MAX:
|
||||
default:
|
||||
pr_err("%d protocol not supported",
|
||||
new_proto->type);
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
st_gdata->list[new_proto->type] = new_proto;
|
||||
add_channel_to_table(st_gdata, new_proto);
|
||||
st_gdata->protos_registered++;
|
||||
new_proto->write = st_write;
|
||||
|
||||
@ -680,48 +586,42 @@ long st_register(struct st_proto_s *new_proto)
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
return err;
|
||||
}
|
||||
pr_debug("done %s(%d) ", __func__, new_proto->type);
|
||||
pr_debug("done %s(%d) ", __func__, new_proto->chnl_id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(st_register);
|
||||
|
||||
/* to unregister a protocol -
|
||||
* to be called from protocol stack driver
|
||||
*/
|
||||
long st_unregister(enum proto_type type)
|
||||
long st_unregister(struct st_proto_s *proto)
|
||||
{
|
||||
long err = 0;
|
||||
unsigned long flags = 0;
|
||||
struct st_data_s *st_gdata;
|
||||
|
||||
pr_debug("%s: %d ", __func__, type);
|
||||
pr_debug("%s: %d ", __func__, proto->chnl_id);
|
||||
|
||||
st_kim_ref(&st_gdata, 0);
|
||||
if (type < ST_BT || type >= ST_MAX) {
|
||||
pr_err(" protocol %d not supported", type);
|
||||
if (proto->chnl_id >= ST_MAX_CHANNELS) {
|
||||
pr_err(" chnl_id %d not supported", proto->chnl_id);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
|
||||
if (st_gdata->list[type] == NULL) {
|
||||
pr_err(" protocol %d not registered", type);
|
||||
if (st_gdata->list[proto->chnl_id] == NULL) {
|
||||
pr_err(" chnl_id %d not registered", proto->chnl_id);
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
st_gdata->protos_registered--;
|
||||
st_gdata->list[type] = NULL;
|
||||
|
||||
/* kim ignores BT in the below function
|
||||
* and handles the rest, BT is toggled
|
||||
* only in kim_start and kim_stop
|
||||
*/
|
||||
st_kim_chip_toggle(type, KIM_GPIO_INACTIVE);
|
||||
remove_channel_from_table(st_gdata, proto);
|
||||
spin_unlock_irqrestore(&st_gdata->lock, flags);
|
||||
|
||||
if ((st_gdata->protos_registered == ST_EMPTY) &&
|
||||
(!test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
|
||||
pr_info(" all protocols unregistered ");
|
||||
pr_info(" all chnl_ids unregistered ");
|
||||
|
||||
/* stop traffic on tty */
|
||||
if (st_gdata->tty) {
|
||||
@ -729,7 +629,7 @@ long st_unregister(enum proto_type type)
|
||||
stop_tty(st_gdata->tty);
|
||||
}
|
||||
|
||||
/* all protocols now unregistered */
|
||||
/* all chnl_ids now unregistered */
|
||||
st_kim_stop(st_gdata->kim_data);
|
||||
/* disable ST LL */
|
||||
st_ll_disable(st_gdata);
|
||||
@ -744,37 +644,15 @@ long st_unregister(enum proto_type type)
|
||||
long st_write(struct sk_buff *skb)
|
||||
{
|
||||
struct st_data_s *st_gdata;
|
||||
#ifdef DEBUG
|
||||
enum proto_type protoid = ST_MAX;
|
||||
#endif
|
||||
long len;
|
||||
|
||||
st_kim_ref(&st_gdata, 0);
|
||||
if (unlikely(skb == NULL || st_gdata == NULL
|
||||
|| st_gdata->tty == NULL)) {
|
||||
pr_err("data/tty unavailable to perform write");
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
#ifdef DEBUG /* open-up skb to read the 1st byte */
|
||||
switch (skb->data[0]) {
|
||||
case HCI_COMMAND_PKT:
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
protoid = ST_BT;
|
||||
break;
|
||||
case ST_FM_CH8_PKT:
|
||||
protoid = ST_FM;
|
||||
break;
|
||||
case 0x09:
|
||||
protoid = ST_GPS;
|
||||
break;
|
||||
}
|
||||
if (unlikely(st_gdata->list[protoid] == NULL)) {
|
||||
pr_err(" protocol %d not registered, and writing? ",
|
||||
protoid);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
pr_debug("%d to be written", skb->len);
|
||||
len = skb->len;
|
||||
|
||||
@ -824,7 +702,7 @@ static int st_tty_open(struct tty_struct *tty)
|
||||
|
||||
static void st_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
unsigned char i = ST_MAX;
|
||||
unsigned char i = ST_MAX_CHANNELS;
|
||||
unsigned long flags = 0;
|
||||
struct st_data_s *st_gdata = tty->disc_data;
|
||||
|
||||
@ -835,7 +713,7 @@ static void st_tty_close(struct tty_struct *tty)
|
||||
* un-installed for some reason - what should be done ?
|
||||
*/
|
||||
spin_lock_irqsave(&st_gdata->lock, flags);
|
||||
for (i = ST_BT; i < ST_MAX; i++) {
|
||||
for (i = ST_BT; i < ST_MAX_CHANNELS; i++) {
|
||||
if (st_gdata->list[i] != NULL)
|
||||
pr_err("%d not un-registered", i);
|
||||
st_gdata->list[i] = NULL;
|
||||
@ -869,7 +747,6 @@ static void st_tty_close(struct tty_struct *tty)
|
||||
static void st_tty_receive(struct tty_struct *tty, const unsigned char *data,
|
||||
char *tty_flags, int count)
|
||||
{
|
||||
|
||||
#ifdef VERBOSE
|
||||
print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE,
|
||||
16, 1, data, count, 0);
|
||||
@ -960,7 +837,7 @@ int st_core_init(struct st_data_s **core_data)
|
||||
err = tty_unregister_ldisc(N_TI_WL);
|
||||
if (err)
|
||||
pr_err("unable to un-register ldisc");
|
||||
return -1;
|
||||
return err;
|
||||
}
|
||||
*core_data = st_gdata;
|
||||
return 0;
|
||||
|
@ -30,50 +30,12 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
/* understand BT events for fw response */
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
#include <linux/tty.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ti_wilink_st.h>
|
||||
|
||||
|
||||
static int kim_probe(struct platform_device *pdev);
|
||||
static int kim_remove(struct platform_device *pdev);
|
||||
|
||||
/* KIM platform device driver structure */
|
||||
static struct platform_driver kim_platform_driver = {
|
||||
.probe = kim_probe,
|
||||
.remove = kim_remove,
|
||||
/* TODO: ST driver power management during suspend/resume ?
|
||||
*/
|
||||
#if 0
|
||||
.suspend = kim_suspend,
|
||||
.resume = kim_resume,
|
||||
#endif
|
||||
.driver = {
|
||||
.name = "kim",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int kim_toggle_radio(void*, bool);
|
||||
static const struct rfkill_ops kim_rfkill_ops = {
|
||||
.set_block = kim_toggle_radio,
|
||||
};
|
||||
|
||||
/* strings to be used for rfkill entries and by
|
||||
* ST Core to be used for sysfs debug entry
|
||||
*/
|
||||
#define PROTO_ENTRY(type, name) name
|
||||
const unsigned char *protocol_names[] = {
|
||||
PROTO_ENTRY(ST_BT, "Bluetooth"),
|
||||
PROTO_ENTRY(ST_FM, "FM"),
|
||||
PROTO_ENTRY(ST_GPS, "GPS"),
|
||||
};
|
||||
|
||||
#define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */
|
||||
static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
|
||||
|
||||
@ -134,7 +96,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len)
|
||||
/* Packet header has non-zero payload length and
|
||||
* we have enough space in created skb. Lets read
|
||||
* payload data */
|
||||
kim_gdata->rx_state = ST_BT_W4_DATA;
|
||||
kim_gdata->rx_state = ST_W4_DATA;
|
||||
kim_gdata->rx_count = len;
|
||||
return len;
|
||||
}
|
||||
@ -158,8 +120,8 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
|
||||
const unsigned char *data, long count)
|
||||
{
|
||||
const unsigned char *ptr;
|
||||
struct hci_event_hdr *eh;
|
||||
int len = 0, type = 0;
|
||||
unsigned char *plen;
|
||||
|
||||
pr_debug("%s", __func__);
|
||||
/* Decode received bytes here */
|
||||
@ -183,29 +145,27 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
|
||||
/* Check ST RX state machine , where are we? */
|
||||
switch (kim_gdata->rx_state) {
|
||||
/* Waiting for complete packet ? */
|
||||
case ST_BT_W4_DATA:
|
||||
case ST_W4_DATA:
|
||||
pr_debug("Complete pkt received");
|
||||
validate_firmware_response(kim_gdata);
|
||||
kim_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
kim_gdata->rx_skb = NULL;
|
||||
continue;
|
||||
/* Waiting for Bluetooth event header ? */
|
||||
case ST_BT_W4_EVENT_HDR:
|
||||
eh = (struct hci_event_hdr *)kim_gdata->
|
||||
rx_skb->data;
|
||||
pr_debug("Event header: evt 0x%2.2x"
|
||||
"plen %d", eh->evt, eh->plen);
|
||||
kim_check_data_len(kim_gdata, eh->plen);
|
||||
case ST_W4_HEADER:
|
||||
plen =
|
||||
(unsigned char *)&kim_gdata->rx_skb->data[1];
|
||||
pr_debug("event hdr: plen 0x%02x\n", *plen);
|
||||
kim_check_data_len(kim_gdata, *plen);
|
||||
continue;
|
||||
} /* end of switch */
|
||||
} /* end of if rx_state */
|
||||
switch (*ptr) {
|
||||
/* Bluetooth event packet? */
|
||||
case HCI_EVENT_PKT:
|
||||
pr_info("Event packet");
|
||||
kim_gdata->rx_state = ST_BT_W4_EVENT_HDR;
|
||||
kim_gdata->rx_count = HCI_EVENT_HDR_SIZE;
|
||||
type = HCI_EVENT_PKT;
|
||||
case 0x04:
|
||||
kim_gdata->rx_state = ST_W4_HEADER;
|
||||
kim_gdata->rx_count = 2;
|
||||
type = *ptr;
|
||||
break;
|
||||
default:
|
||||
pr_info("unknown packet");
|
||||
@ -216,16 +176,18 @@ void kim_int_recv(struct kim_data_s *kim_gdata,
|
||||
ptr++;
|
||||
count--;
|
||||
kim_gdata->rx_skb =
|
||||
bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
alloc_skb(1024+8, GFP_ATOMIC);
|
||||
if (!kim_gdata->rx_skb) {
|
||||
pr_err("can't allocate mem for new packet");
|
||||
kim_gdata->rx_state = ST_W4_PACKET_TYPE;
|
||||
kim_gdata->rx_count = 0;
|
||||
return;
|
||||
}
|
||||
bt_cb(kim_gdata->rx_skb)->pkt_type = type;
|
||||
skb_reserve(kim_gdata->rx_skb, 8);
|
||||
kim_gdata->rx_skb->cb[0] = 4;
|
||||
kim_gdata->rx_skb->cb[1] = 0;
|
||||
|
||||
}
|
||||
pr_info("done %s", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -239,13 +201,13 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
|
||||
INIT_COMPLETION(kim_gdata->kim_rcvd);
|
||||
if (4 != st_int_write(kim_gdata->core_data, read_ver_cmd, 4)) {
|
||||
pr_err("kim: couldn't write 4 bytes");
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!wait_for_completion_timeout
|
||||
(&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
|
||||
pr_err(" waiting for ver info- timed out ");
|
||||
return -1;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
version =
|
||||
@ -270,6 +232,26 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void skip_change_remote_baud(unsigned char **ptr, long *len)
|
||||
{
|
||||
unsigned char *nxt_action, *cur_action;
|
||||
cur_action = *ptr;
|
||||
|
||||
nxt_action = cur_action + sizeof(struct bts_action) +
|
||||
((struct bts_action *) cur_action)->size;
|
||||
|
||||
if (((struct bts_action *) nxt_action)->type != ACTION_WAIT_EVENT) {
|
||||
pr_err("invalid action after change remote baud command");
|
||||
} else {
|
||||
*ptr = *ptr + sizeof(struct bts_action) +
|
||||
((struct bts_action *)nxt_action)->size;
|
||||
*len = *len - (sizeof(struct bts_action) +
|
||||
((struct bts_action *)nxt_action)->size);
|
||||
/* warn user on not commenting these in firmware */
|
||||
pr_warn("skipping the wait event of change remote baud");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* download_firmware -
|
||||
* internal function which parses through the .bts firmware
|
||||
@ -282,6 +264,9 @@ static long download_firmware(struct kim_data_s *kim_gdata)
|
||||
unsigned char *ptr = NULL;
|
||||
unsigned char *action_ptr = NULL;
|
||||
unsigned char bts_scr_name[30] = { 0 }; /* 30 char long bts scr name? */
|
||||
int wr_room_space;
|
||||
int cmd_size;
|
||||
unsigned long timeout;
|
||||
|
||||
err = read_local_version(kim_gdata, bts_scr_name);
|
||||
if (err != 0) {
|
||||
@ -295,7 +280,7 @@ static long download_firmware(struct kim_data_s *kim_gdata)
|
||||
(kim_gdata->fw_entry->size == 0))) {
|
||||
pr_err(" request_firmware failed(errno %ld) for %s", err,
|
||||
bts_scr_name);
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
ptr = (void *)kim_gdata->fw_entry->data;
|
||||
len = kim_gdata->fw_entry->size;
|
||||
@ -318,29 +303,72 @@ static long download_firmware(struct kim_data_s *kim_gdata)
|
||||
0xFF36)) {
|
||||
/* ignore remote change
|
||||
* baud rate HCI VS command */
|
||||
pr_err
|
||||
(" change remote baud"
|
||||
pr_warn("change remote baud"
|
||||
" rate command in firmware");
|
||||
skip_change_remote_baud(&ptr, &len);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Make sure we have enough free space in uart
|
||||
* tx buffer to write current firmware command
|
||||
*/
|
||||
cmd_size = ((struct bts_action *)ptr)->size;
|
||||
timeout = jiffies + msecs_to_jiffies(CMD_WR_TIME);
|
||||
do {
|
||||
wr_room_space =
|
||||
st_get_uart_wr_room(kim_gdata->core_data);
|
||||
if (wr_room_space < 0) {
|
||||
pr_err("Unable to get free "
|
||||
"space info from uart tx buffer");
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return wr_room_space;
|
||||
}
|
||||
mdelay(1); /* wait 1ms before checking room */
|
||||
} while ((wr_room_space < cmd_size) &&
|
||||
time_before(jiffies, timeout));
|
||||
|
||||
INIT_COMPLETION(kim_gdata->kim_rcvd);
|
||||
/* Timeout happened ? */
|
||||
if (time_after_eq(jiffies, timeout)) {
|
||||
pr_err("Timeout while waiting for free "
|
||||
"free space in uart tx buffer");
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free space found in uart buffer, call st_int_write
|
||||
* to send current firmware command to the uart tx
|
||||
* buffer.
|
||||
*/
|
||||
err = st_int_write(kim_gdata->core_data,
|
||||
((struct bts_action_send *)action_ptr)->data,
|
||||
((struct bts_action *)ptr)->size);
|
||||
if (unlikely(err < 0)) {
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return -1;
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Check number of bytes written to the uart tx buffer
|
||||
* and requested command write size
|
||||
*/
|
||||
if (err != cmd_size) {
|
||||
pr_err("Number of bytes written to uart "
|
||||
"tx buffer are not matching with "
|
||||
"requested cmd write size");
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return -EIO;
|
||||
}
|
||||
break;
|
||||
case ACTION_WAIT_EVENT: /* wait */
|
||||
if (!wait_for_completion_timeout
|
||||
(&kim_gdata->kim_rcvd,
|
||||
msecs_to_jiffies(CMD_RESP_TIME))) {
|
||||
pr_err
|
||||
(" response timeout during fw download ");
|
||||
(&kim_gdata->kim_rcvd,
|
||||
msecs_to_jiffies(CMD_RESP_TIME))) {
|
||||
pr_err("response timeout during fw download ");
|
||||
/* timed out */
|
||||
release_firmware(kim_gdata->fw_entry);
|
||||
return -1;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
INIT_COMPLETION(kim_gdata->kim_rcvd);
|
||||
break;
|
||||
case ACTION_DELAY: /* sleep */
|
||||
pr_info("sleep command in scr");
|
||||
@ -362,50 +390,6 @@ static long download_firmware(struct kim_data_s *kim_gdata)
|
||||
|
||||
/**********************************************************************/
|
||||
/* functions called from ST core */
|
||||
/* function to toggle the GPIO
|
||||
* needs to know whether the GPIO is active high or active low
|
||||
*/
|
||||
void st_kim_chip_toggle(enum proto_type type, enum kim_gpio_state state)
|
||||
{
|
||||
struct platform_device *kim_pdev;
|
||||
struct kim_data_s *kim_gdata;
|
||||
pr_info(" %s ", __func__);
|
||||
|
||||
kim_pdev = st_get_plat_device(0);
|
||||
kim_gdata = dev_get_drvdata(&kim_pdev->dev);
|
||||
|
||||
if (kim_gdata->gpios[type] == -1) {
|
||||
pr_info(" gpio not requested for protocol %s",
|
||||
protocol_names[type]);
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case ST_BT:
|
||||
/*Do Nothing */
|
||||
break;
|
||||
|
||||
case ST_FM:
|
||||
if (state == KIM_GPIO_ACTIVE)
|
||||
gpio_set_value(kim_gdata->gpios[ST_FM], GPIO_LOW);
|
||||
else
|
||||
gpio_set_value(kim_gdata->gpios[ST_FM], GPIO_HIGH);
|
||||
break;
|
||||
|
||||
case ST_GPS:
|
||||
if (state == KIM_GPIO_ACTIVE)
|
||||
gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_HIGH);
|
||||
else
|
||||
gpio_set_value(kim_gdata->gpios[ST_GPS], GPIO_LOW);
|
||||
break;
|
||||
|
||||
case ST_MAX:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* called from ST Core, when REG_IN_PROGRESS (registration in progress)
|
||||
* can be because of
|
||||
* 1. response to read local version
|
||||
@ -416,7 +400,6 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count)
|
||||
struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
|
||||
struct kim_data_s *kim_gdata = st_gdata->kim_data;
|
||||
|
||||
pr_info(" %s ", __func__);
|
||||
/* copy to local buffer */
|
||||
if (unlikely(data[4] == 0x01 && data[5] == 0x10 && data[0] == 0x04)) {
|
||||
/* must be the read_ver_cmd */
|
||||
@ -455,35 +438,28 @@ long st_kim_start(void *kim_data)
|
||||
pr_info(" %s", __func__);
|
||||
|
||||
do {
|
||||
/* TODO: this is only because rfkill sub-system
|
||||
* doesn't send events to user-space if the state
|
||||
* isn't changed
|
||||
*/
|
||||
rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1);
|
||||
/* Configure BT nShutdown to HIGH state */
|
||||
gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW);
|
||||
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
|
||||
mdelay(5); /* FIXME: a proper toggle */
|
||||
gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_HIGH);
|
||||
gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
|
||||
mdelay(100);
|
||||
/* re-initialize the completion */
|
||||
INIT_COMPLETION(kim_gdata->ldisc_installed);
|
||||
#if 0 /* older way of signalling user-space UIM */
|
||||
/* send signal to UIM */
|
||||
err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 0);
|
||||
if (err != 0) {
|
||||
pr_info(" sending SIGUSR2 to uim failed %ld", err);
|
||||
err = -1;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* unblock and send event to UIM via /dev/rfkill */
|
||||
rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 0);
|
||||
/* send notification to UIM */
|
||||
kim_gdata->ldisc_install = 1;
|
||||
pr_info("ldisc_install = 1");
|
||||
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
|
||||
NULL, "install");
|
||||
/* wait for ldisc to be installed */
|
||||
err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
|
||||
msecs_to_jiffies(LDISC_TIME));
|
||||
if (!err) { /* timeout */
|
||||
pr_err("line disc installation timed out ");
|
||||
err = -1;
|
||||
kim_gdata->ldisc_install = 0;
|
||||
pr_info("ldisc_install = 0");
|
||||
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
|
||||
NULL, "install");
|
||||
err = -ETIMEDOUT;
|
||||
continue;
|
||||
} else {
|
||||
/* ldisc installed now */
|
||||
@ -491,6 +467,10 @@ long st_kim_start(void *kim_data)
|
||||
err = download_firmware(kim_gdata);
|
||||
if (err != 0) {
|
||||
pr_err("download firmware failed");
|
||||
kim_gdata->ldisc_install = 0;
|
||||
pr_info("ldisc_install = 0");
|
||||
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
|
||||
NULL, "install");
|
||||
continue;
|
||||
} else { /* on success don't retry */
|
||||
break;
|
||||
@ -510,31 +490,30 @@ long st_kim_stop(void *kim_data)
|
||||
struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data;
|
||||
|
||||
INIT_COMPLETION(kim_gdata->ldisc_installed);
|
||||
#if 0 /* older way of signalling user-space UIM */
|
||||
/* send signal to UIM */
|
||||
err = kill_pid(find_get_pid(kim_gdata->uim_pid), SIGUSR2, 1);
|
||||
if (err != 0) {
|
||||
pr_err("sending SIGUSR2 to uim failed %ld", err);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* set BT rfkill to be blocked */
|
||||
err = rfkill_set_hw_state(kim_gdata->rfkill[ST_BT], 1);
|
||||
|
||||
/* Flush any pending characters in the driver and discipline. */
|
||||
tty_ldisc_flush(kim_gdata->core_data->tty);
|
||||
tty_driver_flush_buffer(kim_gdata->core_data->tty);
|
||||
|
||||
/* send uninstall notification to UIM */
|
||||
pr_info("ldisc_install = 0");
|
||||
kim_gdata->ldisc_install = 0;
|
||||
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install");
|
||||
|
||||
/* wait for ldisc to be un-installed */
|
||||
err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
|
||||
msecs_to_jiffies(LDISC_TIME));
|
||||
if (!err) { /* timeout */
|
||||
pr_err(" timed out waiting for ldisc to be un-installed");
|
||||
return -1;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* By default configure BT nShutdown to LOW state */
|
||||
gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW);
|
||||
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
|
||||
mdelay(1);
|
||||
gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_HIGH);
|
||||
gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
|
||||
mdelay(1);
|
||||
gpio_set_value(kim_gdata->gpios[ST_BT], GPIO_LOW);
|
||||
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -558,33 +537,59 @@ static int show_list(struct seq_file *s, void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* function called from rfkill subsystem, when someone from
|
||||
* user space would write 0/1 on the sysfs entry
|
||||
* /sys/class/rfkill/rfkill0,1,3/state
|
||||
*/
|
||||
static int kim_toggle_radio(void *data, bool blocked)
|
||||
static ssize_t show_install(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
enum proto_type type = *((enum proto_type *)data);
|
||||
pr_debug(" %s: %d ", __func__, type);
|
||||
|
||||
switch (type) {
|
||||
case ST_BT:
|
||||
/* do nothing */
|
||||
break;
|
||||
case ST_FM:
|
||||
case ST_GPS:
|
||||
if (blocked)
|
||||
st_kim_chip_toggle(type, KIM_GPIO_INACTIVE);
|
||||
else
|
||||
st_kim_chip_toggle(type, KIM_GPIO_ACTIVE);
|
||||
break;
|
||||
case ST_MAX:
|
||||
pr_err(" wrong proto type ");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
struct kim_data_s *kim_data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%d\n", kim_data->ldisc_install);
|
||||
}
|
||||
|
||||
static ssize_t show_dev_name(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kim_data_s *kim_data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%s\n", kim_data->dev_name);
|
||||
}
|
||||
|
||||
static ssize_t show_baud_rate(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kim_data_s *kim_data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%ld\n", kim_data->baud_rate);
|
||||
}
|
||||
|
||||
static ssize_t show_flow_cntrl(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct kim_data_s *kim_data = dev_get_drvdata(dev);
|
||||
return sprintf(buf, "%d\n", kim_data->flow_cntrl);
|
||||
}
|
||||
|
||||
/* structures specific for sysfs entries */
|
||||
static struct kobj_attribute ldisc_install =
|
||||
__ATTR(install, 0444, (void *)show_install, NULL);
|
||||
|
||||
static struct kobj_attribute uart_dev_name =
|
||||
__ATTR(dev_name, 0444, (void *)show_dev_name, NULL);
|
||||
|
||||
static struct kobj_attribute uart_baud_rate =
|
||||
__ATTR(baud_rate, 0444, (void *)show_baud_rate, NULL);
|
||||
|
||||
static struct kobj_attribute uart_flow_cntrl =
|
||||
__ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL);
|
||||
|
||||
static struct attribute *uim_attrs[] = {
|
||||
&ldisc_install.attr,
|
||||
&uart_dev_name.attr,
|
||||
&uart_baud_rate.attr,
|
||||
&uart_flow_cntrl.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group uim_attr_grp = {
|
||||
.attrs = uim_attrs,
|
||||
};
|
||||
|
||||
/**
|
||||
* st_kim_ref - reference the core's data
|
||||
* This references the per-ST platform device in the arch/xx/
|
||||
@ -637,9 +642,8 @@ struct dentry *kim_debugfs_dir;
|
||||
static int kim_probe(struct platform_device *pdev)
|
||||
{
|
||||
long status;
|
||||
long proto;
|
||||
long *gpios = pdev->dev.platform_data;
|
||||
struct kim_data_s *kim_gdata;
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
|
||||
/* multiple devices could exist */
|
||||
@ -659,44 +663,24 @@ static int kim_probe(struct platform_device *pdev)
|
||||
status = st_core_init(&kim_gdata->core_data);
|
||||
if (status != 0) {
|
||||
pr_err(" ST core init failed");
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
/* refer to itself */
|
||||
kim_gdata->core_data->kim_data = kim_gdata;
|
||||
|
||||
for (proto = 0; proto < ST_MAX; proto++) {
|
||||
kim_gdata->gpios[proto] = gpios[proto];
|
||||
pr_info(" %ld gpio to be requested", gpios[proto]);
|
||||
/* Claim the chip enable nShutdown gpio from the system */
|
||||
kim_gdata->nshutdown = pdata->nshutdown_gpio;
|
||||
status = gpio_request(kim_gdata->nshutdown, "kim");
|
||||
if (unlikely(status)) {
|
||||
pr_err(" gpio %ld request failed ", kim_gdata->nshutdown);
|
||||
return status;
|
||||
}
|
||||
|
||||
for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
|
||||
/* Claim the Bluetooth/FM/GPIO
|
||||
* nShutdown gpio from the system
|
||||
*/
|
||||
status = gpio_request(gpios[proto], "kim");
|
||||
if (unlikely(status)) {
|
||||
pr_err(" gpio %ld request failed ", gpios[proto]);
|
||||
proto -= 1;
|
||||
while (proto >= 0) {
|
||||
if (gpios[proto] != -1)
|
||||
gpio_free(gpios[proto]);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Configure nShutdown GPIO as output=0 */
|
||||
status =
|
||||
gpio_direction_output(gpios[proto], 0);
|
||||
if (unlikely(status)) {
|
||||
pr_err(" unable to configure gpio %ld",
|
||||
gpios[proto]);
|
||||
proto -= 1;
|
||||
while (proto >= 0) {
|
||||
if (gpios[proto] != -1)
|
||||
gpio_free(gpios[proto]);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
/* Configure nShutdown GPIO as output=0 */
|
||||
status = gpio_direction_output(kim_gdata->nshutdown, 0);
|
||||
if (unlikely(status)) {
|
||||
pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown);
|
||||
return status;
|
||||
}
|
||||
/* get reference of pdev for request_firmware
|
||||
*/
|
||||
@ -704,34 +688,23 @@ static int kim_probe(struct platform_device *pdev)
|
||||
init_completion(&kim_gdata->kim_rcvd);
|
||||
init_completion(&kim_gdata->ldisc_installed);
|
||||
|
||||
for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
|
||||
/* TODO: should all types be rfkill_type_bt ? */
|
||||
kim_gdata->rf_protos[proto] = proto;
|
||||
kim_gdata->rfkill[proto] = rfkill_alloc(protocol_names[proto],
|
||||
&pdev->dev, RFKILL_TYPE_BLUETOOTH,
|
||||
&kim_rfkill_ops, &kim_gdata->rf_protos[proto]);
|
||||
if (kim_gdata->rfkill[proto] == NULL) {
|
||||
pr_err("cannot create rfkill entry for gpio %ld",
|
||||
gpios[proto]);
|
||||
continue;
|
||||
}
|
||||
/* block upon creation */
|
||||
rfkill_init_sw_state(kim_gdata->rfkill[proto], 1);
|
||||
status = rfkill_register(kim_gdata->rfkill[proto]);
|
||||
if (unlikely(status)) {
|
||||
pr_err("rfkill registration failed for gpio %ld",
|
||||
gpios[proto]);
|
||||
rfkill_unregister(kim_gdata->rfkill[proto]);
|
||||
continue;
|
||||
}
|
||||
pr_info("rfkill entry created for %ld", gpios[proto]);
|
||||
status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp);
|
||||
if (status) {
|
||||
pr_err("failed to create sysfs entries");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* copying platform data */
|
||||
strncpy(kim_gdata->dev_name, pdata->dev_name, UART_DEV_NAME_LEN);
|
||||
kim_gdata->flow_cntrl = pdata->flow_cntrl;
|
||||
kim_gdata->baud_rate = pdata->baud_rate;
|
||||
pr_info("sysfs entries created\n");
|
||||
|
||||
kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
|
||||
if (IS_ERR(kim_debugfs_dir)) {
|
||||
pr_err(" debugfs entries creation failed ");
|
||||
kim_debugfs_dir = NULL;
|
||||
return -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
debugfs_create_file("version", S_IRUGO, kim_debugfs_dir,
|
||||
@ -744,25 +717,22 @@ static int kim_probe(struct platform_device *pdev)
|
||||
|
||||
static int kim_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* free the GPIOs requested
|
||||
*/
|
||||
long *gpios = pdev->dev.platform_data;
|
||||
long proto;
|
||||
/* free the GPIOs requested */
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
struct kim_data_s *kim_gdata;
|
||||
|
||||
kim_gdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
for (proto = 0; (proto < ST_MAX) && (gpios[proto] != -1); proto++) {
|
||||
/* Claim the Bluetooth/FM/GPIO
|
||||
* nShutdown gpio from the system
|
||||
*/
|
||||
gpio_free(gpios[proto]);
|
||||
rfkill_unregister(kim_gdata->rfkill[proto]);
|
||||
rfkill_destroy(kim_gdata->rfkill[proto]);
|
||||
kim_gdata->rfkill[proto] = NULL;
|
||||
}
|
||||
pr_info("kim: GPIO Freed");
|
||||
/* Free the Bluetooth/FM/GPIO
|
||||
* nShutdown gpio from the system
|
||||
*/
|
||||
gpio_free(pdata->nshutdown_gpio);
|
||||
pr_info("nshutdown GPIO Freed");
|
||||
|
||||
debugfs_remove_recursive(kim_debugfs_dir);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
|
||||
pr_info("sysfs entries removed");
|
||||
|
||||
kim_gdata->kim_pdev = NULL;
|
||||
st_core_exit(kim_gdata->core_data);
|
||||
|
||||
@ -771,23 +741,46 @@ static int kim_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kim_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdata->suspend)
|
||||
return pdata->suspend(pdev, state);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int kim_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdata->resume)
|
||||
return pdata->resume(pdev);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* entry point for ST KIM module, called in from ST Core */
|
||||
static struct platform_driver kim_platform_driver = {
|
||||
.probe = kim_probe,
|
||||
.remove = kim_remove,
|
||||
.suspend = kim_suspend,
|
||||
.resume = kim_resume,
|
||||
.driver = {
|
||||
.name = "kim",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init st_kim_init(void)
|
||||
{
|
||||
long ret = 0;
|
||||
ret = platform_driver_register(&kim_platform_driver);
|
||||
if (ret != 0) {
|
||||
pr_err("platform drv registration failed");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return platform_driver_register(&kim_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit st_kim_deinit(void)
|
||||
{
|
||||
/* the following returns void */
|
||||
platform_driver_unregister(&kim_platform_driver);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ static void send_ll_cmd(struct st_data_s *st_data,
|
||||
unsigned char cmd)
|
||||
{
|
||||
|
||||
pr_info("%s: writing %x", __func__, cmd);
|
||||
pr_debug("%s: writing %x", __func__, cmd);
|
||||
st_int_write(st_data, &cmd, 1);
|
||||
return;
|
||||
}
|
||||
@ -114,23 +114,23 @@ unsigned long st_ll_sleep_state(struct st_data_s *st_data,
|
||||
{
|
||||
switch (cmd) {
|
||||
case LL_SLEEP_IND: /* sleep ind */
|
||||
pr_info("sleep indication recvd");
|
||||
pr_debug("sleep indication recvd");
|
||||
ll_device_want_to_sleep(st_data);
|
||||
break;
|
||||
case LL_SLEEP_ACK: /* sleep ack */
|
||||
pr_err("sleep ack rcvd: host shouldn't");
|
||||
break;
|
||||
case LL_WAKE_UP_IND: /* wake ind */
|
||||
pr_info("wake indication recvd");
|
||||
pr_debug("wake indication recvd");
|
||||
ll_device_want_to_wakeup(st_data);
|
||||
break;
|
||||
case LL_WAKE_UP_ACK: /* wake ack */
|
||||
pr_info("wake ack rcvd");
|
||||
pr_debug("wake ack rcvd");
|
||||
st_data->ll_state = ST_LL_AWAKE;
|
||||
break;
|
||||
default:
|
||||
pr_err(" unknown input/state ");
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -94,4 +94,21 @@ config UIO_NETX
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called uio_netx.
|
||||
|
||||
config UIO_PRUSS
|
||||
tristate "Texas Instruments PRUSS driver"
|
||||
depends on ARCH_DAVINCI_DA850
|
||||
help
|
||||
PRUSS driver for OMAPL138/DA850/AM18XX devices
|
||||
PRUSS driver requires user space components, examples and user space
|
||||
driver is available from below SVN repo - you may use anonymous login
|
||||
|
||||
https://gforge.ti.com/gf/project/pru_sw/
|
||||
|
||||
More info on API is available at below wiki
|
||||
|
||||
http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called uio_pruss.
|
||||
|
||||
endif
|
||||
|
@ -6,3 +6,4 @@ obj-$(CONFIG_UIO_AEC) += uio_aec.o
|
||||
obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
|
||||
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
|
||||
obj-$(CONFIG_UIO_NETX) += uio_netx.o
|
||||
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
|
||||
|
247
drivers/uio/uio_pruss.c
Normal file
247
drivers/uio/uio_pruss.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Programmable Real-Time Unit Sub System (PRUSS) UIO driver (uio_pruss)
|
||||
*
|
||||
* This driver exports PRUSS host event out interrupts and PRUSS, L3 RAM,
|
||||
* and DDR RAM to user space for applications interacting with PRUSS firmware
|
||||
*
|
||||
* Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uio_driver.h>
|
||||
#include <linux/platform_data/uio_pruss.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <mach/sram.h>
|
||||
|
||||
#define DRV_NAME "pruss_uio"
|
||||
#define DRV_VERSION "1.0"
|
||||
|
||||
static int sram_pool_sz = SZ_16K;
|
||||
module_param(sram_pool_sz, int, 0);
|
||||
MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate ");
|
||||
|
||||
static int extram_pool_sz = SZ_256K;
|
||||
module_param(extram_pool_sz, int, 0);
|
||||
MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate");
|
||||
|
||||
/*
|
||||
* Host event IRQ numbers from PRUSS - PRUSS can generate upto 8 interrupt
|
||||
* events to AINTC of ARM host processor - which can be used for IPC b/w PRUSS
|
||||
* firmware and user space application, async notification from PRU firmware
|
||||
* to user space application
|
||||
* 3 PRU_EVTOUT0
|
||||
* 4 PRU_EVTOUT1
|
||||
* 5 PRU_EVTOUT2
|
||||
* 6 PRU_EVTOUT3
|
||||
* 7 PRU_EVTOUT4
|
||||
* 8 PRU_EVTOUT5
|
||||
* 9 PRU_EVTOUT6
|
||||
* 10 PRU_EVTOUT7
|
||||
*/
|
||||
#define MAX_PRUSS_EVT 8
|
||||
|
||||
#define PINTC_HIDISR 0x0038
|
||||
#define PINTC_HIPIR 0x0900
|
||||
#define HIPIR_NOPEND 0x80000000
|
||||
#define PINTC_HIER 0x1500
|
||||
|
||||
struct uio_pruss_dev {
|
||||
struct uio_info *info;
|
||||
struct clk *pruss_clk;
|
||||
dma_addr_t sram_paddr;
|
||||
dma_addr_t ddr_paddr;
|
||||
void __iomem *prussio_vaddr;
|
||||
void *sram_vaddr;
|
||||
void *ddr_vaddr;
|
||||
unsigned int hostirq_start;
|
||||
unsigned int pintc_base;
|
||||
};
|
||||
|
||||
static irqreturn_t pruss_handler(int irq, struct uio_info *info)
|
||||
{
|
||||
struct uio_pruss_dev *gdev = info->priv;
|
||||
int intr_bit = (irq - gdev->hostirq_start + 2);
|
||||
int val, intr_mask = (1 << intr_bit);
|
||||
void __iomem *base = gdev->prussio_vaddr + gdev->pintc_base;
|
||||
void __iomem *intren_reg = base + PINTC_HIER;
|
||||
void __iomem *intrdis_reg = base + PINTC_HIDISR;
|
||||
void __iomem *intrstat_reg = base + PINTC_HIPIR + (intr_bit << 2);
|
||||
|
||||
val = ioread32(intren_reg);
|
||||
/* Is interrupt enabled and active ? */
|
||||
if (!(val & intr_mask) && (ioread32(intrstat_reg) & HIPIR_NOPEND))
|
||||
return IRQ_NONE;
|
||||
/* Disable interrupt */
|
||||
iowrite32(intr_bit, intrdis_reg);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void pruss_cleanup(struct platform_device *dev,
|
||||
struct uio_pruss_dev *gdev)
|
||||
{
|
||||
int cnt;
|
||||
struct uio_info *p = gdev->info;
|
||||
|
||||
for (cnt = 0; cnt < MAX_PRUSS_EVT; cnt++, p++) {
|
||||
uio_unregister_device(p);
|
||||
kfree(p->name);
|
||||
}
|
||||
iounmap(gdev->prussio_vaddr);
|
||||
if (gdev->ddr_vaddr) {
|
||||
dma_free_coherent(&dev->dev, extram_pool_sz, gdev->ddr_vaddr,
|
||||
gdev->ddr_paddr);
|
||||
}
|
||||
if (gdev->sram_vaddr)
|
||||
sram_free(gdev->sram_vaddr, sram_pool_sz);
|
||||
kfree(gdev->info);
|
||||
clk_put(gdev->pruss_clk);
|
||||
kfree(gdev);
|
||||
}
|
||||
|
||||
static int __devinit pruss_probe(struct platform_device *dev)
|
||||
{
|
||||
struct uio_info *p;
|
||||
struct uio_pruss_dev *gdev;
|
||||
struct resource *regs_prussio;
|
||||
int ret = -ENODEV, cnt = 0, len;
|
||||
struct uio_pruss_pdata *pdata = dev->dev.platform_data;
|
||||
|
||||
gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL);
|
||||
if (!gdev)
|
||||
return -ENOMEM;
|
||||
|
||||
gdev->info = kzalloc(sizeof(*p) * MAX_PRUSS_EVT, GFP_KERNEL);
|
||||
if (!gdev->info) {
|
||||
kfree(gdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Power on PRU in case its not done as part of boot-loader */
|
||||
gdev->pruss_clk = clk_get(&dev->dev, "pruss");
|
||||
if (IS_ERR(gdev->pruss_clk)) {
|
||||
dev_err(&dev->dev, "Failed to get clock\n");
|
||||
kfree(gdev->info);
|
||||
kfree(gdev);
|
||||
ret = PTR_ERR(gdev->pruss_clk);
|
||||
return ret;
|
||||
} else {
|
||||
clk_enable(gdev->pruss_clk);
|
||||
}
|
||||
|
||||
regs_prussio = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!regs_prussio) {
|
||||
dev_err(&dev->dev, "No PRUSS I/O resource specified\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!regs_prussio->start) {
|
||||
dev_err(&dev->dev, "Invalid memory resource\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr));
|
||||
if (!gdev->sram_vaddr) {
|
||||
dev_err(&dev->dev, "Could not allocate SRAM pool\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
gdev->ddr_vaddr = dma_alloc_coherent(&dev->dev, extram_pool_sz,
|
||||
&(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA);
|
||||
if (!gdev->ddr_vaddr) {
|
||||
dev_err(&dev->dev, "Could not allocate external memory\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
len = resource_size(regs_prussio);
|
||||
gdev->prussio_vaddr = ioremap(regs_prussio->start, len);
|
||||
if (!gdev->prussio_vaddr) {
|
||||
dev_err(&dev->dev, "Can't remap PRUSS I/O address range\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
gdev->pintc_base = pdata->pintc_base;
|
||||
gdev->hostirq_start = platform_get_irq(dev, 0);
|
||||
|
||||
for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) {
|
||||
p->mem[0].addr = regs_prussio->start;
|
||||
p->mem[0].size = resource_size(regs_prussio);
|
||||
p->mem[0].memtype = UIO_MEM_PHYS;
|
||||
|
||||
p->mem[1].addr = gdev->sram_paddr;
|
||||
p->mem[1].size = sram_pool_sz;
|
||||
p->mem[1].memtype = UIO_MEM_PHYS;
|
||||
|
||||
p->mem[2].addr = gdev->ddr_paddr;
|
||||
p->mem[2].size = extram_pool_sz;
|
||||
p->mem[2].memtype = UIO_MEM_PHYS;
|
||||
|
||||
p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", cnt);
|
||||
p->version = DRV_VERSION;
|
||||
|
||||
/* Register PRUSS IRQ lines */
|
||||
p->irq = gdev->hostirq_start + cnt;
|
||||
p->handler = pruss_handler;
|
||||
p->priv = gdev;
|
||||
|
||||
ret = uio_register_device(&dev->dev, p);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, gdev);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
pruss_cleanup(dev, gdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pruss_remove(struct platform_device *dev)
|
||||
{
|
||||
struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
|
||||
|
||||
pruss_cleanup(dev, gdev);
|
||||
platform_set_drvdata(dev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pruss_driver = {
|
||||
.probe = pruss_probe,
|
||||
.remove = __devexit_p(pruss_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pruss_init_module(void)
|
||||
{
|
||||
return platform_driver_register(&pruss_driver);
|
||||
}
|
||||
|
||||
module_init(pruss_init_module);
|
||||
|
||||
static void __exit pruss_exit_module(void)
|
||||
{
|
||||
platform_driver_unregister(&pruss_driver);
|
||||
}
|
||||
|
||||
module_exit(pruss_exit_module);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
MODULE_AUTHOR("Amit Chatterjee <amit.chatterjee@ti.com>");
|
||||
MODULE_AUTHOR("Pratheesh Gangadhar <pratheesh@ti.com>");
|
@ -13,9 +13,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* uncomment to get debug messages from the debug filesystem, ah the irony. */
|
||||
/* #define DEBUG */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
@ -310,7 +307,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_symlink);
|
||||
|
||||
static void __debugfs_remove(struct dentry *dentry, struct dentry *parent)
|
||||
static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
@ -333,6 +330,7 @@ static void __debugfs_remove(struct dentry *dentry, struct dentry *parent)
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -351,7 +349,8 @@ static void __debugfs_remove(struct dentry *dentry, struct dentry *parent)
|
||||
void debugfs_remove(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent;
|
||||
|
||||
int ret;
|
||||
|
||||
if (!dentry)
|
||||
return;
|
||||
|
||||
@ -360,9 +359,10 @@ void debugfs_remove(struct dentry *dentry)
|
||||
return;
|
||||
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
__debugfs_remove(dentry, parent);
|
||||
ret = __debugfs_remove(dentry, parent);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
if (!ret)
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_remove);
|
||||
|
||||
@ -540,17 +540,5 @@ static int __init debugfs_init(void)
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit debugfs_exit(void)
|
||||
{
|
||||
debugfs_registered = false;
|
||||
|
||||
simple_release_fs(&debugfs_mount, &debugfs_mount_count);
|
||||
unregister_filesystem(&debug_fs_type);
|
||||
kobject_put(debug_kobj);
|
||||
}
|
||||
|
||||
core_initcall(debugfs_init);
|
||||
module_exit(debugfs_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -23,6 +23,53 @@ enum dmi_device_type {
|
||||
DMI_DEV_TYPE_DEV_ONBOARD = -3,
|
||||
};
|
||||
|
||||
enum dmi_entry_type {
|
||||
DMI_ENTRY_BIOS = 0,
|
||||
DMI_ENTRY_SYSTEM,
|
||||
DMI_ENTRY_BASEBOARD,
|
||||
DMI_ENTRY_CHASSIS,
|
||||
DMI_ENTRY_PROCESSOR,
|
||||
DMI_ENTRY_MEM_CONTROLLER,
|
||||
DMI_ENTRY_MEM_MODULE,
|
||||
DMI_ENTRY_CACHE,
|
||||
DMI_ENTRY_PORT_CONNECTOR,
|
||||
DMI_ENTRY_SYSTEM_SLOT,
|
||||
DMI_ENTRY_ONBOARD_DEVICE,
|
||||
DMI_ENTRY_OEMSTRINGS,
|
||||
DMI_ENTRY_SYSCONF,
|
||||
DMI_ENTRY_BIOS_LANG,
|
||||
DMI_ENTRY_GROUP_ASSOC,
|
||||
DMI_ENTRY_SYSTEM_EVENT_LOG,
|
||||
DMI_ENTRY_PHYS_MEM_ARRAY,
|
||||
DMI_ENTRY_MEM_DEVICE,
|
||||
DMI_ENTRY_32_MEM_ERROR,
|
||||
DMI_ENTRY_MEM_ARRAY_MAPPED_ADDR,
|
||||
DMI_ENTRY_MEM_DEV_MAPPED_ADDR,
|
||||
DMI_ENTRY_BUILTIN_POINTING_DEV,
|
||||
DMI_ENTRY_PORTABLE_BATTERY,
|
||||
DMI_ENTRY_SYSTEM_RESET,
|
||||
DMI_ENTRY_HW_SECURITY,
|
||||
DMI_ENTRY_SYSTEM_POWER_CONTROLS,
|
||||
DMI_ENTRY_VOLTAGE_PROBE,
|
||||
DMI_ENTRY_COOLING_DEV,
|
||||
DMI_ENTRY_TEMP_PROBE,
|
||||
DMI_ENTRY_ELECTRICAL_CURRENT_PROBE,
|
||||
DMI_ENTRY_OOB_REMOTE_ACCESS,
|
||||
DMI_ENTRY_BIS_ENTRY,
|
||||
DMI_ENTRY_SYSTEM_BOOT,
|
||||
DMI_ENTRY_MGMT_DEV,
|
||||
DMI_ENTRY_MGMT_DEV_COMPONENT,
|
||||
DMI_ENTRY_MGMT_DEV_THRES,
|
||||
DMI_ENTRY_MEM_CHANNEL,
|
||||
DMI_ENTRY_IPMI_DEV,
|
||||
DMI_ENTRY_SYS_POWER_SUPPLY,
|
||||
DMI_ENTRY_ADDITIONAL,
|
||||
DMI_ENTRY_ONBOARD_DEV_EXT,
|
||||
DMI_ENTRY_MGMT_CONTROLLER_HOST,
|
||||
DMI_ENTRY_INACTIVE = 126,
|
||||
DMI_ENTRY_END_OF_TABLE = 127,
|
||||
};
|
||||
|
||||
struct dmi_header {
|
||||
u8 type;
|
||||
u8 length;
|
||||
|
@ -31,6 +31,10 @@ struct _ddebug {
|
||||
* writes commands to <debugfs>/dynamic_debug/control
|
||||
*/
|
||||
#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */
|
||||
#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
|
||||
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
|
||||
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
|
||||
#define _DPRINTK_FLAGS_INCL_TID (1<<4)
|
||||
#define _DPRINTK_FLAGS_DEFAULT 0
|
||||
unsigned int flags:8;
|
||||
char enabled;
|
||||
@ -42,6 +46,8 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n,
|
||||
|
||||
#if defined(CONFIG_DYNAMIC_DEBUG)
|
||||
extern int ddebug_remove_module(const char *mod_name);
|
||||
extern int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
#define dynamic_pr_debug(fmt, ...) do { \
|
||||
static struct _ddebug descriptor \
|
||||
@ -50,7 +56,7 @@ extern int ddebug_remove_module(const char *mod_name);
|
||||
{ KBUILD_MODNAME, __func__, __FILE__, fmt, __LINE__, \
|
||||
_DPRINTK_FLAGS_DEFAULT }; \
|
||||
if (unlikely(descriptor.enabled)) \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \
|
||||
__dynamic_pr_debug(&descriptor, pr_fmt(fmt), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
@ -397,4 +397,41 @@ static inline void memrange_efi_to_native(u64 *addr, u64 *npages)
|
||||
*addr &= PAGE_MASK;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_EFI_VARS) || defined(CONFIG_EFI_VARS_MODULE)
|
||||
/*
|
||||
* EFI Variable support.
|
||||
*
|
||||
* Different firmware drivers can expose their EFI-like variables using
|
||||
* the following.
|
||||
*/
|
||||
|
||||
struct efivar_operations {
|
||||
efi_get_variable_t *get_variable;
|
||||
efi_get_next_variable_t *get_next_variable;
|
||||
efi_set_variable_t *set_variable;
|
||||
};
|
||||
|
||||
struct efivars {
|
||||
/*
|
||||
* ->lock protects two things:
|
||||
* 1) ->list - adds, removals, reads, writes
|
||||
* 2) ops.[gs]et_variable() calls.
|
||||
* It must not be held when creating sysfs entries or calling kmalloc.
|
||||
* ops.get_next_variable() is only called from register_efivars(),
|
||||
* which is protected by the BKL, so that path is safe.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
struct kset *kset;
|
||||
struct bin_attribute *new_var, *del_var;
|
||||
const struct efivar_operations *ops;
|
||||
};
|
||||
|
||||
int register_efivars(struct efivars *efivars,
|
||||
const struct efivar_operations *ops,
|
||||
struct kobject *parent_kobj);
|
||||
void unregister_efivars(struct efivars *efivars);
|
||||
|
||||
#endif /* CONFIG_EFI_VARS */
|
||||
|
||||
#endif /* _LINUX_EFI_H */
|
||||
|
@ -39,7 +39,7 @@ struct builtin_fw {
|
||||
int request_firmware(const struct firmware **fw, const char *name,
|
||||
struct device *device);
|
||||
int request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
struct module *module, bool uevent,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context));
|
||||
|
||||
@ -52,7 +52,7 @@ static inline int request_firmware(const struct firmware **fw,
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int request_firmware_nowait(
|
||||
struct module *module, int uevent,
|
||||
struct module *module, bool uevent,
|
||||
const char *name, struct device *device, gfp_t gfp, void *context,
|
||||
void (*cont)(const struct firmware *fw, void *context))
|
||||
{
|
||||
|
@ -85,11 +85,13 @@ static inline const char *kobject_name(const struct kobject *kobj)
|
||||
extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
|
||||
extern int __must_check kobject_add(struct kobject *kobj,
|
||||
struct kobject *parent,
|
||||
const char *fmt, ...);
|
||||
const char *fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
extern int __must_check kobject_init_and_add(struct kobject *kobj,
|
||||
struct kobj_type *ktype,
|
||||
struct kobject *parent,
|
||||
const char *fmt, ...);
|
||||
const char *fmt, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
|
||||
extern void kobject_del(struct kobject *kobj);
|
||||
|
||||
@ -224,8 +226,8 @@ static inline int kobject_uevent_env(struct kobject *kobj,
|
||||
char *envp[])
|
||||
{ return 0; }
|
||||
|
||||
static inline int add_uevent_var(struct kobj_uevent_env *env,
|
||||
const char *format, ...)
|
||||
static inline __attribute__((format(printf, 2, 3)))
|
||||
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
|
||||
{ return 0; }
|
||||
|
||||
static inline int kobject_action_type(const char *buf, size_t count,
|
||||
|
@ -21,7 +21,8 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
struct memory_block {
|
||||
unsigned long phys_index;
|
||||
unsigned long start_section_nr;
|
||||
unsigned long end_section_nr;
|
||||
unsigned long state;
|
||||
int section_count;
|
||||
|
||||
|
@ -39,7 +39,8 @@ extern int register_cpu_under_node(unsigned int cpu, unsigned int nid);
|
||||
extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid);
|
||||
extern int register_mem_sect_under_node(struct memory_block *mem_blk,
|
||||
int nid);
|
||||
extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk);
|
||||
extern int unregister_mem_sect_under_nodes(struct memory_block *mem_blk,
|
||||
unsigned long phys_index);
|
||||
|
||||
#ifdef CONFIG_HUGETLBFS
|
||||
extern void register_hugetlbfs_with_node(node_registration_func_t doregister,
|
||||
@ -67,7 +68,8 @@ static inline int register_mem_sect_under_node(struct memory_block *mem_blk,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk)
|
||||
static inline int unregister_mem_sect_under_nodes(struct memory_block *mem_blk,
|
||||
unsigned long phys_index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
25
include/linux/platform_data/uio_pruss.h
Normal file
25
include/linux/platform_data/uio_pruss.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* include/linux/platform_data/uio_pruss.h
|
||||
*
|
||||
* Platform data for uio_pruss driver
|
||||
*
|
||||
* Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _UIO_PRUSS_H_
|
||||
#define _UIO_PRUSS_H_
|
||||
|
||||
/* To configure the PRUSS INTC base offset for UIO driver */
|
||||
struct uio_pruss_pdata {
|
||||
u32 pintc_base;
|
||||
};
|
||||
#endif /* _UIO_PRUSS_H_ */
|
@ -130,8 +130,15 @@ extern void platform_driver_unregister(struct platform_driver *);
|
||||
extern int platform_driver_probe(struct platform_driver *driver,
|
||||
int (*probe)(struct platform_device *));
|
||||
|
||||
#define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev)
|
||||
#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data))
|
||||
static inline void *platform_get_drvdata(const struct platform_device *pdev)
|
||||
{
|
||||
return dev_get_drvdata(&pdev->dev);
|
||||
}
|
||||
|
||||
static inline void platform_set_drvdata(struct platform_device *pdev, void *data)
|
||||
{
|
||||
dev_set_drvdata(&pdev->dev, data);
|
||||
}
|
||||
|
||||
extern struct platform_device *platform_create_bundle(struct platform_driver *driver,
|
||||
int (*probe)(struct platform_device *),
|
||||
|
@ -25,15 +25,6 @@
|
||||
#ifndef TI_WILINK_ST_H
|
||||
#define TI_WILINK_ST_H
|
||||
|
||||
/**
|
||||
* enum kim_gpio_state - Few protocols such as FM have ACTIVE LOW
|
||||
* gpio states for their chip/core enable gpios
|
||||
*/
|
||||
enum kim_gpio_state {
|
||||
KIM_GPIO_INACTIVE,
|
||||
KIM_GPIO_ACTIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum proto-type - The protocol on WiLink chips which share a
|
||||
* common physical interface like UART.
|
||||
@ -42,7 +33,7 @@ enum proto_type {
|
||||
ST_BT,
|
||||
ST_FM,
|
||||
ST_GPS,
|
||||
ST_MAX,
|
||||
ST_MAX_CHANNELS = 16,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -62,6 +53,17 @@ enum proto_type {
|
||||
* @priv_data: privdate data holder for the protocol drivers, sent
|
||||
* from the protocol drivers during registration, and sent back on
|
||||
* reg_complete_cb and recv.
|
||||
* @chnl_id: channel id the protocol driver is interested in, the channel
|
||||
* id is nothing but the 1st byte of the packet in UART frame.
|
||||
* @max_frame_size: size of the largest frame the protocol can receive.
|
||||
* @hdr_len: length of the header structure of the protocol.
|
||||
* @offset_len_in_hdr: this provides the offset of the length field in the
|
||||
* header structure of the protocol header, to assist ST to know
|
||||
* how much to receive, if the data is split across UART frames.
|
||||
* @len_size: whether the length field inside the header is 2 bytes
|
||||
* or 1 byte.
|
||||
* @reserve: the number of bytes ST needs to reserve in the skb being
|
||||
* prepared for the protocol driver.
|
||||
*/
|
||||
struct st_proto_s {
|
||||
enum proto_type type;
|
||||
@ -70,10 +72,17 @@ struct st_proto_s {
|
||||
void (*reg_complete_cb) (void *, char data);
|
||||
long (*write) (struct sk_buff *skb);
|
||||
void *priv_data;
|
||||
|
||||
unsigned char chnl_id;
|
||||
unsigned short max_frame_size;
|
||||
unsigned char hdr_len;
|
||||
unsigned char offset_len_in_hdr;
|
||||
unsigned char len_size;
|
||||
unsigned char reserve;
|
||||
};
|
||||
|
||||
extern long st_register(struct st_proto_s *);
|
||||
extern long st_unregister(enum proto_type);
|
||||
extern long st_unregister(struct st_proto_s *);
|
||||
|
||||
|
||||
/*
|
||||
@ -114,6 +123,7 @@ extern long st_unregister(enum proto_type);
|
||||
* @rx_skb: the skb where all data for a protocol gets accumulated,
|
||||
* since tty might not call receive when a complete event packet
|
||||
* is received, the states, count and the skb needs to be maintained.
|
||||
* @rx_chnl: the channel ID for which the data is getting accumalated for.
|
||||
* @txq: the list of skbs which needs to be sent onto the TTY.
|
||||
* @tx_waitq: if the chip is not in AWAKE state, the skbs needs to be queued
|
||||
* up in here, PM(WAKEUP_IND) data needs to be sent and then the skbs
|
||||
@ -135,10 +145,11 @@ struct st_data_s {
|
||||
#define ST_TX_SENDING 1
|
||||
#define ST_TX_WAKEUP 2
|
||||
unsigned long tx_state;
|
||||
struct st_proto_s *list[ST_MAX];
|
||||
struct st_proto_s *list[ST_MAX_CHANNELS];
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
unsigned char rx_chnl;
|
||||
struct sk_buff_head txq, tx_waitq;
|
||||
spinlock_t lock;
|
||||
unsigned char protos_registered;
|
||||
@ -146,6 +157,11 @@ struct st_data_s {
|
||||
void *kim_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* wrapper around tty->ops->write_room to check
|
||||
* availability during firmware download
|
||||
*/
|
||||
int st_get_uart_wr_room(struct st_data_s *st_gdata);
|
||||
/**
|
||||
* st_int_write -
|
||||
* point this to tty->driver->write or tty->ops->write
|
||||
@ -186,8 +202,9 @@ void gps_chrdrv_stub_init(void);
|
||||
/* time in msec to wait for
|
||||
* line discipline to be installed
|
||||
*/
|
||||
#define LDISC_TIME 500
|
||||
#define CMD_RESP_TIME 500
|
||||
#define LDISC_TIME 1000
|
||||
#define CMD_RESP_TIME 800
|
||||
#define CMD_WR_TIME 5000
|
||||
#define MAKEWORD(a, b) ((unsigned short)(((unsigned char)(a)) \
|
||||
| ((unsigned short)((unsigned char)(b))) << 8))
|
||||
|
||||
@ -210,6 +227,7 @@ struct chip_version {
|
||||
unsigned short maj_ver;
|
||||
};
|
||||
|
||||
#define UART_DEV_NAME_LEN 32
|
||||
/**
|
||||
* struct kim_data_s - the KIM internal data, embedded as the
|
||||
* platform's drv data. One for each ST device in the system.
|
||||
@ -225,14 +243,11 @@ struct chip_version {
|
||||
* the ldisc was properly installed.
|
||||
* @resp_buffer: data buffer for the .bts fw file name.
|
||||
* @fw_entry: firmware class struct to request/release the fw.
|
||||
* @gpios: the list of core/chip enable gpios for BT, FM and GPS cores.
|
||||
* @rx_state: the rx state for kim's receive func during fw download.
|
||||
* @rx_count: the rx count for the kim's receive func during fw download.
|
||||
* @rx_skb: all of fw data might not come at once, and hence data storage for
|
||||
* whole of the fw response, only HCI_EVENTs and hence diff from ST's
|
||||
* response.
|
||||
* @rfkill: rfkill data for each of the cores to be registered with rfkill.
|
||||
* @rf_protos: proto types of the data registered with rfkill sub-system.
|
||||
* @core_data: ST core's data, which mainly is the tty's disc_data
|
||||
* @version: chip version available via a sysfs entry.
|
||||
*
|
||||
@ -243,14 +258,16 @@ struct kim_data_s {
|
||||
struct completion kim_rcvd, ldisc_installed;
|
||||
char resp_buffer[30];
|
||||
const struct firmware *fw_entry;
|
||||
long gpios[ST_MAX];
|
||||
long nshutdown;
|
||||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
struct rfkill *rfkill[ST_MAX];
|
||||
enum proto_type rf_protos[ST_MAX];
|
||||
struct st_data_s *core_data;
|
||||
struct chip_version version;
|
||||
unsigned char ldisc_install;
|
||||
unsigned char dev_name[UART_DEV_NAME_LEN];
|
||||
unsigned char flow_cntrl;
|
||||
unsigned long baud_rate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -262,7 +279,6 @@ long st_kim_start(void *);
|
||||
long st_kim_stop(void *);
|
||||
|
||||
void st_kim_recv(void *, const unsigned char *, long count);
|
||||
void st_kim_chip_toggle(enum proto_type, enum kim_gpio_state);
|
||||
void st_kim_complete(void *);
|
||||
void kim_st_list_protocols(struct st_data_s *, void *);
|
||||
|
||||
@ -338,12 +354,8 @@ struct hci_command {
|
||||
|
||||
/* ST LL receiver states */
|
||||
#define ST_W4_PACKET_TYPE 0
|
||||
#define ST_BT_W4_EVENT_HDR 1
|
||||
#define ST_BT_W4_ACL_HDR 2
|
||||
#define ST_BT_W4_SCO_HDR 3
|
||||
#define ST_BT_W4_DATA 4
|
||||
#define ST_FM_W4_EVENT_HDR 5
|
||||
#define ST_GPS_W4_EVENT_HDR 6
|
||||
#define ST_W4_HEADER 1
|
||||
#define ST_W4_DATA 2
|
||||
|
||||
/* ST LL state machines */
|
||||
#define ST_LL_ASLEEP 0
|
||||
@ -397,4 +409,14 @@ struct gps_event_hdr {
|
||||
u16 plen;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* platform data */
|
||||
struct ti_st_plat_data {
|
||||
long nshutdown_gpio;
|
||||
unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */
|
||||
unsigned char flow_cntrl; /* flow control flag */
|
||||
unsigned long baud_rate;
|
||||
int (*suspend)(struct platform_device *, pm_message_t);
|
||||
int (*resume)(struct platform_device *);
|
||||
};
|
||||
|
||||
#endif /* TI_WILINK_ST_H */
|
||||
|
@ -836,7 +836,7 @@ config MM_OWNER
|
||||
bool
|
||||
|
||||
config SYSFS_DEPRECATED
|
||||
bool "enable deprecated sysfs features to support old userspace tools"
|
||||
bool "Enable deprecated sysfs features to support old userspace tools"
|
||||
depends on SYSFS
|
||||
default n
|
||||
help
|
||||
@ -859,7 +859,7 @@ config SYSFS_DEPRECATED
|
||||
need to say Y here.
|
||||
|
||||
config SYSFS_DEPRECATED_V2
|
||||
bool "enabled deprecated sysfs features by default"
|
||||
bool "Enable deprecated sysfs features by default"
|
||||
default n
|
||||
depends on SYSFS
|
||||
depends on SYSFS_DEPRECATED
|
||||
|
138
kernel/printk.c
138
kernel/printk.c
@ -514,6 +514,71 @@ static void _call_console_drivers(unsigned start,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the syslog header <[0-9]*>. The decimal value represents 32bit, the
|
||||
* lower 3 bit are the log level, the rest are the log facility. In case
|
||||
* userspace passes usual userspace syslog messages to /dev/kmsg or
|
||||
* /dev/ttyprintk, the log prefix might contain the facility. Printk needs
|
||||
* to extract the correct log level for in-kernel processing, and not mangle
|
||||
* the original value.
|
||||
*
|
||||
* If a prefix is found, the length of the prefix is returned. If 'level' is
|
||||
* passed, it will be filled in with the log level without a possible facility
|
||||
* value. If 'special' is passed, the special printk prefix chars are accepted
|
||||
* and returned. If no valid header is found, 0 is returned and the passed
|
||||
* variables are not touched.
|
||||
*/
|
||||
static size_t log_prefix(const char *p, unsigned int *level, char *special)
|
||||
{
|
||||
unsigned int lev = 0;
|
||||
char sp = '\0';
|
||||
size_t len;
|
||||
|
||||
if (p[0] != '<' || !p[1])
|
||||
return 0;
|
||||
if (p[2] == '>') {
|
||||
/* usual single digit level number or special char */
|
||||
switch (p[1]) {
|
||||
case '0' ... '7':
|
||||
lev = p[1] - '0';
|
||||
break;
|
||||
case 'c': /* KERN_CONT */
|
||||
case 'd': /* KERN_DEFAULT */
|
||||
sp = p[1];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
len = 3;
|
||||
} else {
|
||||
/* multi digit including the level and facility number */
|
||||
char *endp = NULL;
|
||||
|
||||
if (p[1] < '0' && p[1] > '9')
|
||||
return 0;
|
||||
|
||||
lev = (simple_strtoul(&p[1], &endp, 10) & 7);
|
||||
if (endp == NULL || endp[0] != '>')
|
||||
return 0;
|
||||
len = (endp + 1) - p;
|
||||
}
|
||||
|
||||
/* do not accept special char if not asked for */
|
||||
if (sp && !special)
|
||||
return 0;
|
||||
|
||||
if (special) {
|
||||
*special = sp;
|
||||
/* return special char, do not touch level */
|
||||
if (sp)
|
||||
return len;
|
||||
}
|
||||
|
||||
if (level)
|
||||
*level = lev;
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the console drivers, asking them to write out
|
||||
* log_buf[start] to log_buf[end - 1].
|
||||
@ -529,13 +594,9 @@ static void call_console_drivers(unsigned start, unsigned end)
|
||||
cur_index = start;
|
||||
start_print = start;
|
||||
while (cur_index != end) {
|
||||
if (msg_level < 0 && ((end - cur_index) > 2) &&
|
||||
LOG_BUF(cur_index + 0) == '<' &&
|
||||
LOG_BUF(cur_index + 1) >= '0' &&
|
||||
LOG_BUF(cur_index + 1) <= '7' &&
|
||||
LOG_BUF(cur_index + 2) == '>') {
|
||||
msg_level = LOG_BUF(cur_index + 1) - '0';
|
||||
cur_index += 3;
|
||||
if (msg_level < 0 && ((end - cur_index) > 2)) {
|
||||
/* strip log prefix */
|
||||
cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL);
|
||||
start_print = cur_index;
|
||||
}
|
||||
while (cur_index != end) {
|
||||
@ -733,6 +794,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
|
||||
unsigned long flags;
|
||||
int this_cpu;
|
||||
char *p;
|
||||
size_t plen;
|
||||
char special;
|
||||
|
||||
boot_delay_msec();
|
||||
printk_delay();
|
||||
@ -773,45 +836,52 @@ asmlinkage int vprintk(const char *fmt, va_list args)
|
||||
printed_len += vscnprintf(printk_buf + printed_len,
|
||||
sizeof(printk_buf) - printed_len, fmt, args);
|
||||
|
||||
|
||||
p = printk_buf;
|
||||
|
||||
/* Do we have a loglevel in the string? */
|
||||
if (p[0] == '<') {
|
||||
unsigned char c = p[1];
|
||||
if (c && p[2] == '>') {
|
||||
switch (c) {
|
||||
case '0' ... '7': /* loglevel */
|
||||
current_log_level = c - '0';
|
||||
/* Fallthrough - make sure we're on a new line */
|
||||
case 'd': /* KERN_DEFAULT */
|
||||
if (!new_text_line) {
|
||||
emit_log_char('\n');
|
||||
new_text_line = 1;
|
||||
}
|
||||
/* Fallthrough - skip the loglevel */
|
||||
case 'c': /* KERN_CONT */
|
||||
p += 3;
|
||||
break;
|
||||
/* Read log level and handle special printk prefix */
|
||||
plen = log_prefix(p, ¤t_log_level, &special);
|
||||
if (plen) {
|
||||
p += plen;
|
||||
|
||||
switch (special) {
|
||||
case 'c': /* Strip <c> KERN_CONT, continue line */
|
||||
plen = 0;
|
||||
break;
|
||||
case 'd': /* Strip <d> KERN_DEFAULT, start new line */
|
||||
plen = 0;
|
||||
default:
|
||||
if (!new_text_line) {
|
||||
emit_log_char('\n');
|
||||
new_text_line = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the output into log_buf. If the caller didn't provide
|
||||
* appropriate log level tags, we insert them here
|
||||
* Copy the output into log_buf. If the caller didn't provide
|
||||
* the appropriate log prefix, we insert them here
|
||||
*/
|
||||
for ( ; *p; p++) {
|
||||
for (; *p; p++) {
|
||||
if (new_text_line) {
|
||||
/* Always output the token */
|
||||
emit_log_char('<');
|
||||
emit_log_char(current_log_level + '0');
|
||||
emit_log_char('>');
|
||||
printed_len += 3;
|
||||
new_text_line = 0;
|
||||
|
||||
if (plen) {
|
||||
/* Copy original log prefix */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < plen; i++)
|
||||
emit_log_char(printk_buf[i]);
|
||||
printed_len += plen;
|
||||
} else {
|
||||
/* Add log prefix */
|
||||
emit_log_char('<');
|
||||
emit_log_char(current_log_level + '0');
|
||||
emit_log_char('>');
|
||||
printed_len += 3;
|
||||
}
|
||||
|
||||
if (printk_time) {
|
||||
/* Follow the token with the time */
|
||||
/* Add the current time stamp */
|
||||
char tbuf[50], *tp;
|
||||
unsigned tlen;
|
||||
unsigned long long t;
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Copyright (C) 2008 Jason Baron <jbaron@redhat.com>
|
||||
* By Greg Banks <gnb@melbourne.sgi.com>
|
||||
* Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved.
|
||||
* Copyright (C) 2011 Bart Van Assche. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -27,6 +28,8 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
extern struct _ddebug __start___verbose[];
|
||||
extern struct _ddebug __stop___verbose[];
|
||||
@ -63,15 +66,25 @@ static inline const char *basename(const char *path)
|
||||
return tail ? tail+1 : path;
|
||||
}
|
||||
|
||||
static struct { unsigned flag:8; char opt_char; } opt_array[] = {
|
||||
{ _DPRINTK_FLAGS_PRINT, 'p' },
|
||||
{ _DPRINTK_FLAGS_INCL_MODNAME, 'm' },
|
||||
{ _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' },
|
||||
{ _DPRINTK_FLAGS_INCL_LINENO, 'l' },
|
||||
{ _DPRINTK_FLAGS_INCL_TID, 't' },
|
||||
};
|
||||
|
||||
/* format a string into buf[] which describes the _ddebug's flags */
|
||||
static char *ddebug_describe_flags(struct _ddebug *dp, char *buf,
|
||||
size_t maxlen)
|
||||
{
|
||||
char *p = buf;
|
||||
int i;
|
||||
|
||||
BUG_ON(maxlen < 4);
|
||||
if (dp->flags & _DPRINTK_FLAGS_PRINT)
|
||||
*p++ = 'p';
|
||||
for (i = 0; i < ARRAY_SIZE(opt_array); ++i)
|
||||
if (dp->flags & opt_array[i].flag)
|
||||
*p++ = opt_array[i].opt_char;
|
||||
if (p == buf)
|
||||
*p++ = '-';
|
||||
*p = '\0';
|
||||
@ -343,7 +356,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
|
||||
unsigned int *maskp)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
int op = '=';
|
||||
int op = '=', i;
|
||||
|
||||
switch (*str) {
|
||||
case '+':
|
||||
@ -358,13 +371,14 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp,
|
||||
printk(KERN_INFO "%s: op='%c'\n", __func__, op);
|
||||
|
||||
for ( ; *str ; ++str) {
|
||||
switch (*str) {
|
||||
case 'p':
|
||||
flags |= _DPRINTK_FLAGS_PRINT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) {
|
||||
if (*str == opt_array[i].opt_char) {
|
||||
flags |= opt_array[i].flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (flags == 0)
|
||||
return -EINVAL;
|
||||
@ -413,6 +427,35 @@ static int ddebug_exec_query(char *query_string)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int res;
|
||||
|
||||
BUG_ON(!descriptor);
|
||||
BUG_ON(!fmt);
|
||||
|
||||
va_start(args, fmt);
|
||||
res = printk(KERN_DEBUG);
|
||||
if (descriptor->flags & _DPRINTK_FLAGS_INCL_TID) {
|
||||
if (in_interrupt())
|
||||
res += printk(KERN_CONT "<intr> ");
|
||||
else
|
||||
res += printk(KERN_CONT "[%d] ", task_pid_vnr(current));
|
||||
}
|
||||
if (descriptor->flags & _DPRINTK_FLAGS_INCL_MODNAME)
|
||||
res += printk(KERN_CONT "%s:", descriptor->modname);
|
||||
if (descriptor->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
|
||||
res += printk(KERN_CONT "%s:", descriptor->function);
|
||||
if (descriptor->flags & _DPRINTK_FLAGS_INCL_LINENO)
|
||||
res += printk(KERN_CONT "%d ", descriptor->lineno);
|
||||
res += vprintk(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(__dynamic_pr_debug);
|
||||
|
||||
static __initdata char ddebug_setup_string[1024];
|
||||
static __init int ddebug_setup_query(char *str)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user