《linux设备驱动开发详解》笔记——18 ARM linux设备树
18.1 设备树的起源
linux 2.6及之前,大量板级信息被硬编码到内核里,十分庞大,大量冗余代码;
linux 2.6之前,引入了设备树;
设备树源于OpenFirmware,描述硬件的数据结构。由一些列节点node和属性property组成,通常包括下列信息:
本质上是画一棵CPU、总线、设备组成的树,Linux内核会把设备树展开成platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、中断等资源,也会传递个内核,内核会将这些资源绑定给展开的相应设备。
18.2 设备树的组成和结构
18.2.1 DTS/DTC/DTB
dts:文本
dtc:编译dts的
dtb:编译后的二进制文件
绑定:documentation/devicetree/bindings/***,描述各硬件的dts格式,不一定及时更新,但可作为参考。
以下为dts的结构:
描述上图属性结构的示例dts为:
/ {
compatible = "acme,coyotes-rev
#address-cells = <1>; // 描述下一级子节点的数据属性
#size-cells = <1>;
interrupt-parent = <&intc>;
7 cpus {
#address-cells = <>;
#size-cells = <>;
cpu@ {
compatible = "arm,cortex-a9"; // device兼容性,用于与driver匹配
reg = <>;
};
cpu@ {
compatible = "arm,cortex-a9";
reg = <>;
};
}; serial@101f0000 { // 地址
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < >;
}; serial@101f2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < >;
}; 32 gpio@101f3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < >;
}; intc: interrupt-controller@ {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <>;
}; spi@ {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < >; };
51
external-bus {
#address-cells = <>
54 #size-cells = <>;
ranges = < 0x10100000 0x10000 // Chipselect 1, Ethernet
0x10160000 0x10000 // Chipselect 2, i2c controller
0x30000000 0x1000000>; // Chipselect 3, NOR Flash
58
ethernet@, {
compatible = "smc,smc91c111";
reg = < 0x1000>;
interrupts = < >;
}; i2c@, {
compatible = "acme,a1234-i2c-bus";
67 #address-cells = <>;
#size-cells = <>;
reg = < 0x1000>;
interrupts = < >;
rtc@ {
compatible = "maxim,ds1338";
reg = <>;
interrupts = < >;
};
}; flash@, {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = < 0x4000000>;
};
};// end of external-bus
};
18.2.2 根节点兼容性
18.2.2.1 实现芯片型号相关初始化
根节点的兼容性,一般格式如下(以ZYNQ为例):
dts:
compatible = "xlnx,zynq-7000"; // 厂商、型号
内核代码:
static const char * const zynq_dt_match[] = {
"xlnx,zynq-7000",
NULL
};
// 匹配后,DT_MACHINE_START到MACHINE_END之间的函数会被执行,arch/arm/mach-zynq/common.c
DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = zynq_irq_init,
.init_machine = zynq_init_machine,
.init_late = zynq_init_late,
.init_time = zynq_timer_init,
.dt_compat = zynq_dt_match,
.reserve = zynq_memory_init,
.restart = zynq_system_reset,
MACHINE_END #define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~, \
.name = _namestr, #define MACHINE_END \
};
18.2.2.2 提倡同类型号归一处理
同一类型的芯片,可以使用同一的初始化接口,用OF的API判断根节点兼容性字段,走不同分支。
/**
* of_machine_is_compatible - Test root of device tree for a given compatible value
* @compat: compatible string to look for in root node's compatible property.
*
* Returns true if the root node has the given value in its
* compatible property.
*/
int of_machine_is_compatible(const char *compat)
{
struct device_node *root;
int rc = ; root = of_find_node_by_path("/");
if (root) {
rc = of_device_is_compatible(root, compat);
of_node_put(root);
}
return rc;
}
EXPORT_SYMBOL(of_machine_is_compatible);
18.2.3 设备节点兼容性
表示device的兼容性, 用于与driver匹配。
zynq i2c匹配示例 dts:
ps7_i2c_1: ps7-i2c@e0005000 {
clock-frequency = <>;
clocks = <&clkc >;
compatible = "cdns,i2c-r1p10";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < >;
reg = <0xe0005000 0x1000>;
xlnx,has-interrupt = <0x0>;
#address-cells = <>;
#size-cells = <>;
eeprom@ {
compatible = "at,24c512";
reg = <0x52>;
};
} ; static const struct of_device_id cdns_i2c_of_match[] = {
{ .compatible = "cdns,i2c-r1p10", },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match); static struct platform_driver cdns_i2c_drv = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = cdns_i2c_of_match,
.pm = &cdns_i2c_dev_pm_ops,
},
.probe = cdns_i2c_probe,
.remove = cdns_i2c_remove,
}; // i2c总线负责匹配
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type); static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver; if (!client)
return ; /* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return ; /* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return ; driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL; return ;
}
18.2.4 设备节点及label的命名
设备节点采用 <name>[@<unit-address>]的格式,[ ]为可选项。挂到内存空间的设备,unit-address一般是内存地址。
还可以为节点创建标签,别的地方引用时可以用标签。
ps7_gpio_0: ps7-gpio@e000a000 { // 标签 : 节点
#gpio-cells = <>;
clocks = <&clkc >;
compatible = "xlnx,zynq-gpio-1.0";
emio-gpio-width = <>;
gpio-controller ;
gpio-mask-high = <0x0>;
gpio-mask-low = <0x5600>;
interrupt-parent = <&ps7_scugic_0>;
interrupts = < >;
reg = <0xe000a000 0x1000>;
} ; ps7_ethernet_0: ps7-ethernet@e000b000 {
...
enet-reset = <&ps7_gpio_0 >; // 引用标签
...
} ;
18.2.5 地址编码
1. #address-cells和#size-cells
// 父节点的address-cells和size-cells决定子节点的reg的address和lenth字段的长度,cell的单位为32bit
#address-cells // 子节点reg的address为几个32bit的整型数据
#size-cells // 长度为几个32bit整型数据,如果为0,则没有lenth字段 node{
reg = <address1 lenth1 [address2 lenth2] [address3 lenth3]...>;
}
ps7_axi_interconnect_0: amba@ {
#address-cells = <1>; // 下一级节点reg的address字段长度为1个32bit,下一级节点为ps7-i2c@e0005000
#size-cells = <1>; // 下一级节点reg的len字段长度为1个32bit ...
ps7_i2c_1: ps7-i2c@e0005000 {
clock-frequency = <>;
clocks = <&clkc >;
compatible = "cdns,i2c-r1p10";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < >;
reg = <0xe0005000 0x1000>; // 地址为0xe0005000,长度为0x1000
xlnx,has-interrupt = <0x0>;
#address-cells = <>; // 下一级eeprom的reg属性
#size-cells = <>; // 没有len字段
eeprom@ {
compatible = "at,24c512";
reg = <0x52>; // 地址为0x52,没有长度字段
};
} ;
...
}
18.2.6 中断连接
- interrutt-controller,属性为空,表明“我是中断控制器”
- #interrupt-cells,表明连接此中断控制器的设备的中断属性的cell大小,也就是interrupt = <>属性的cell大小
- interrupt-parent,设备节点通过这个关键字指定其依附的中断控制器phandle,如果没有指定,则继承父节点的interrupt-parent配置
- interrupt,设备节点里使用,一般包含中断号、触发方法等。具体有多少个cell,由#interrupt-cells决定,每个cell的具体含义,一般由驱动的实决定,一般会在绑定文件里说明,下面是arm interrupt的绑定文件:3个cell,1st是种类(spi/ppi),2st是中断号,3st是flag
Documentation/devicetree/bindings/arm/gic.txt * ARM Generic Interrupt Controller ARM SMP cores are often associated with a GIC, providing per processor
interrupts (PPI), shared processor interrupts (SPI) and software
generated interrupts (SGI). Primary GIC is attached directly to the CPU and typically has PPIs and SGIs.
Secondary GICs are cascaded into the upward interrupt controller and do not
have PPIs or SGIs. Main node required properties: - compatible : should be one of:
"arm,gic-400"
"arm,cortex-a15-gic"
"arm,cortex-a9-gic"
"arm,cortex-a7-gic"
"arm,arm11mp-gic"
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The type shall be a <u32> and the value shall be . The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts. The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15]. The 3rd cell is the flags, encoded as follows:
bits[:] trigger type and level flags.
= low-to-high edge triggered
= high-to-low edge triggered
= active high level-sensitive
= active low level-sensitive
bits[:] PPI interrupt cpu mask. Each bit corresponds to each of
the possible cpus attached to the GIC. A bit set to '' indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts. - reg : Specifies base physical address(s) and size of the GIC registers. The
first region is the GIC distributor register base and size. The 2nd region is
the GIC cpu interface register base and size. Optional
- interrupts : Interrupt source of the parent interrupt controller on
secondary GICs, or VGIC maintenance interrupt on primary GIC (see
below). - cpu-offset : per-cpu offset within the distributor and cpu interface
regions, used when the GIC doesn't have banked registers. The offset is
cpu-offset * cpu-nr. Example: intc: interrupt-controller@fff11000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <>;
#address-cells = <>;
interrupt-controller;
reg = <0xfff11000 0x1000>,
<0xfff10100 0x100>;
}; * GIC virtualization extensions (VGIC) For ARM cores that support the virtualization extensions, additional
properties must be described (they only exist if the GIC is the
primary interrupt controller). Required properties: - reg : Additional regions specifying the base physical address and
size of the VGIC registers. The first additional region is the GIC
virtual interface control register base and size. The 2nd additional
region is the GIC virtual cpu interface register base and size. - interrupts : VGIC maintenance interrupt. Example: interrupt-controller@2c001000 {
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <>;
interrupt-controller;
reg = <0x2c001000 0x1000>,
<0x2c002000 0x1000>,
<0x2c004000 0x2000>,
<0x2c006000 0x2000>;
interrupts = < 0xf04>;
};
ps7_scugic_0: ps7-scugic@f8f01000 {
#address-cells = <>;
#interrupt-cells = <3>; // 设备节点的interrupt属性有3个cells
#size-cells = <>;
compatible = "arm,cortex-a9-gic", "arm,gic";
interrupt-controller ; // 我是中断控制器
num_cpus = <>;
num_interrupts = <>;
reg = <0xf8f01000 0x1000>, <0xf8f00100 0x100>;
} ;
ps7_i2c_1: ps7-i2c@e0005000 {
clock-frequency = <400000>;
clocks = <&clkc 39>;
compatible = "cdns,i2c-r1p10";
interrupt-parent = <&ps7_scugic_0>;
interrupts = <0 48 4>; // 0,spi中断;48号中断,4表示高电平触发
reg = <0xe0005000 0x1000>;
xlnx,has-interrupt = <0x0>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@52 {
compatible = "at,24c512";
reg = <0x52>;
};
} ;
18.2.7 GPIO、时钟、pinmux连接
1. GPIO
- dts的GPIO描述
gpio-controller表示“我是GPIO控制器”;
#gpio-cells,设置GPIO属性的大小
ps7_gpio_0: ps7-gpio@e000a000 {
#gpio-cells = <2>; // 2个cell,一般第一个表示用哪个GPIO,第二个数0表示高电平有效、1表示低电平有效
clocks = <&clkc >;
compatible = "xlnx,zynq-gpio-1.0";
emio-gpio-width = <>;
gpio-controller ;
gpio-mask-high = <0x0>;
gpio-mask-low = <0x5600>;
interrupt-parent = <&ps7_scugic_0>;
interrupts = < >;
reg = <0xe000a000 0x1000>;
} ; ps7_ethernet_0: ps7-ethernet@e000b000 {
...
enet-reset = <&ps7_gpio_0 11 0>;
...
} ;
- 驱动中用name获取GPIO
/**
* of_get_named_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
* @propname: Name of property containing gpio specifier(s)
* @index: index of the GPIO
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index); 例如:
of_get_named_gpio(np,"enet-reset",0);
- 驱动中用编号获取GPIO
/**
* of_get_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
* @index: index of the GPIO
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
static inline int of_get_gpio(struct device_node *np, int index):
2. 时钟
ps7_slcr_0: ps7-slcr@f8000000 {
#address-cells = <>;
#size-cells = <>;
compatible = "xlnx,zynq-slcr", "syscon";
ranges ;
reg = <0xf8000000 0x1000>;
clkc: clkc@ {
#clock-cells = <>;
clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x",
"cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci",
"lqspi", "smc", "pcap", "gem0", "gem1",
"fclk0", "fclk1", "fclk2", "fclk3", "can0",
"can1", "sdio0", "sdio1", "uart0", "uart1",
"spi0", "spi1", "dma", "usb0_aper", "usb1_aper",
"gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper",
"spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper",
"uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper",
"swdt", "dbg_trc", "dbg_apb";
compatible = "xlnx,ps7-clkc";
fclk-enable = <0xf>;
ps-clk-frequency = <>;
reg = <0x100 0x100>;
} ;
} ; ps7_spi_1: ps7-spi@e0007000 {
clock-names = "ref_clk", "pclk"; // 用于接口函数获取时钟
clocks = <&clkc >, <&clkc >; // 后面的数字对应上面clock-output-names数组的index
...
};
static int cdns_spi_probe(struct platform_device *pdev)
{
...
xspi->pclk = devm_clk_get(&pdev->dev, "pclk"); // 通过别名获取时钟
if (IS_ERR(xspi->pclk)) {
dev_err(&pdev->dev, "pclk clock not found.\n");
ret = PTR_ERR(xspi->pclk);
goto remove_master;
} xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
if (IS_ERR(xspi->ref_clk)) {
dev_err(&pdev->dev, "ref_clk clock not found.\n");
ret = PTR_ERR(xspi->ref_clk);
goto remove_master;
}
...
}
3. pinmux
18.3 由设备树引起的BSP和驱动变更
1. 设备自动展开,设备信息不再需要硬编码
根节点匹配以后,会自动展开device,resouce中的信息来源与dts的reg、interrupt等字段。 static const char * const zynq_dt_match[] = {
"xlnx,zynq-7000",
NULL
}; DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
.smp = smp_ops(zynq_smp_ops),
.map_io = zynq_map_io,
.init_irq = zynq_irq_init,
.init_machine = zynq_init_machine,
.init_late = zynq_init_late,
.init_time = zynq_timer_init,
.dt_compat = zynq_dt_match,
.reserve = zynq_memory_init,
.restart = zynq_system_reset,
MACHINE_END /**
* zynq_init_machine - System specific initialization, intended to be
* called from board specific initialization.
*/
static void __init zynq_init_machine(void)
{
struct platform_device_info devinfo = { .name = "cpufreq-cpu0", }; of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); platform_device_register(&zynq_cpuidle_device);
platform_device_register_full(&devinfo); zynq_slcr_init();
}
18.4 常用的OF API
// 1. 寻找节点:根据兼容属性,获取设备节点。遍历设备节点,匹配设备类型和兼容属性,多数情况下,from和type都填NULL
extern struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compat);
// 2.读取属性
// (1)读取属性名为propname的整型数组
extern int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz);
extern int of_property_read_u16_array(const struct device_node *np,
const char *propname, u16 *out_values, size_t sz);
extern int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz);
extern int of_property_read_u64(const struct device_node *np,
const char *propname, u64 *out_value); static void __init pl310_of_setup(const struct device_node *np,
u32 *aux_val, u32 *aux_mask)
{
u32 data[] = { , , };
u32 tag[] = { , , };
u32 filter[] = { , }; of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
if (tag[] && tag[] && tag[])
writel_relaxed(
((tag[] - ) << L2X0_LATENCY_CTRL_RD_SHIFT) |
((tag[] - ) << L2X0_LATENCY_CTRL_WR_SHIFT) |
((tag[] - ) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
l2x0_base + L2X0_TAG_LATENCY_CTRL); of_property_read_u32_array(np, "arm,data-latency",
data, ARRAY_SIZE(data));
if (data[] && data[] && data[])
writel_relaxed(
((data[] - ) << L2X0_LATENCY_CTRL_RD_SHIFT) |
((data[] - ) << L2X0_LATENCY_CTRL_WR_SHIFT) |
((data[] - ) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
l2x0_base + L2X0_DATA_LATENCY_CTRL); of_property_read_u32_array(np, "arm,filter-ranges",
filter, ARRAY_SIZE(filter));
if (filter[]) {
writel_relaxed(ALIGN(filter[] + filter[], SZ_1M),
l2x0_base + L2X0_ADDR_FILTER_END);
writel_relaxed((filter[] & ~(SZ_1M - )) | L2X0_ADDR_FILTER_EN,
l2x0_base + L2X0_ADDR_FILTER_START);
}
} //dts
ps7_pl310_0: ps7-pl310@f8f02000 {
arm,data-latency = < >;
arm,tag-latency = < >;
cache-level = <>;
cache-unified ;
compatible = "arm,pl310-cache";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < >;
reg = <0xf8f02000 0x1000>;
} ;
// (2) 读取字符串,注意指向指针的指针
extern int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string); // 读取属性中第index个字符串
int of_property_read_string_index(struct device_node *np, const char *propname,
int index, const char **output);
// (3) 读取bool
/**
* of_property_read_bool - Findfrom a property
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
*
* Search for a property in a device node.
* Returns true if the property exist false otherwise.
*/
static inline bool of_property_read_bool(const struct device_node *np,
const char *propname)
{
struct property *prop = of_find_property(np, propname, NULL); return prop ? true : false;
}
// 3 reg字段内存映射
/**
* of_iomap - Maps the memory mapped IO for a given device_node,对dts的reg字段进行内存映射
* @device: the device whose io range will be mapped
* @index: index of the io range,reg可能有多段,0代表第一段
*
* Returns a pointer to the mapped memory
*/
void __iomem *of_iomap(struct device_node *np, int index)
{
struct resource res; if (of_address_to_resource(np, index, &res))
return NULL; return ioremap(res.start, resource_size(&res));
}
// 4 解析中断
/*
* irq_of_parse_and_map() is used by all OF enabled platforms; but SPARC
* implements it differently. However, the prototype is the same for all,
* so declare it here regardless of the CONFIG_OF_IRQ setting.
* 实质是从dts的interrupt字段解析出中断号,如果有多个中断,index指定索引号
*/
extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
// 5 获取与节点对应的platform_device
// (1). node------->platform_device
/**
* of_find_device_by_node - Find the platform_device associated with a node
* @np: Pointer to device tree node
*
* Returns platform_device pointer, or NULL if not found
*/
struct platform_device *of_find_device_by_node(struct device_node *np)
{
struct device *dev; dev = bus_find_device(&platform_bus_type, NULL, np, of_dev_node_match);
return dev ? to_platform_device(dev) : NULL;
} // (2). platform_device------->node
static int xxx_probe( struct platform_device * pdev )
{
struct device_node *node = pdev->dev.op_node;
}
18.5 总结
《linux设备驱动开发详解》笔记——18 ARM linux设备树的更多相关文章
- linux设备驱动开发详解 笔记
在目录的 Makefile 中关于 RTC_DRV_S3C 的编译脚本为: obj -$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o 上述脚本意味着如果 RTC_DRV_S3 ...
- Linux设备驱动开发详解
Linux设备驱动开发详解 http://download.csdn.net/detail/wuyouzi067/9581380
- 《Linux设备驱动开发详解(第2版)》配套视频登录51cto教育频道
http://edu.51cto.com/course/course_id-379-page-1.html http://edu.51cto.com/course/course_id-379-page ...
- 《linux设备驱动开发详解》笔记——6字符设备驱动
6.1 字符设备驱动结构 先看看字符设备驱动的架构: 6.1.1 cdev cdev结构体是字符设备的核心数据结构,用于描述一个字符设备,cdev定义如下: #include <linux/cd ...
- 《linux设备驱动开发详解》笔记——15 linux i2c驱动
结合实际代码和书中描述,可能跟书上有一定出入.本文后续芯片相关代码参考ZYNQ. 15.1 总体结构 如下图,i2c驱动分为如下几个重要模块 核心层core,完成i2c总线.设备.驱动模型,对用户提供 ...
- 《linux设备驱动开发详解》笔记——14 linux网络设备驱动
14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx(); 注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...
- 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想
本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...
- 《linux设备驱动开发详解》笔记——10中断与时钟
10.1 中断与定时器 中断一般有如下类型: 内部中断和外部中断:内部中断来自CPU,例如软件中断指令.溢出.除0错误等:外部中断有外部设备触发 可屏蔽中断和不可屏蔽中断 向量中断和非向量中断,ARM ...
- Linux设备驱动开发详解-Note(11)--- Linux 文件系统与设备文件系统(3)
Linux 文件系统与设备文件系统(3) 成于坚持,败于止步 sysfs 文件系统与 Linux 设备模型 1.sysfs 文件系统 Linux 2.6 内核引入了 sysfs 文件系统,sysfs ...
随机推荐
- shell 发送Post请求,并获取状态码
#!/bin/bash aa=$ result=$(curl -H "Content-type: application/json" -X POST -o /dev/null -s ...
- Linux 解压压缩war包
jar -xvf aaa.war (jar只能解压war 包到当前目录下) unzip aaa.war -d aaa/ (解压war包到aaa目录下) 打包aaa 下的所有文件为aaa.war ...
- openstack安装newton版本neutron服务部署(四)
一.管理节点部署服务: 1.安装neutron: [root@linux-node1 ~]# yum install openstack-neutron openstack-neutron-ml2 o ...
- Exception sending context destroyed event to listener instance of class
五月 29, 2019 6:29:39 下午 org.apache.catalina.core.StandardContext listenerStop严重: Exception sending co ...
- EF 记录执行的sql语句
最近做了个中等的项目,数据不会很多,开发时间比较紧迫,所以用了EF的框架. 在使用过程中,发现有时候执行的结果不如预期,想看看执行的sql语句为何,遍查找资料,在网上找到了相关辅助类,拿来使用,部署到 ...
- dubbo注解
如果还不了解Dubbo是什么或者不知道怎么搭建的可以先看一下我的上一篇文章. 首先我先来讲下提供者(也就是服务端)的配置,先上配置文件代码: <?xml version="1.0&qu ...
- 从wireshark数据中分析rtmp协议,并提取出H264视频流
我写的小工具 rtmp_parse.exe 使用用法如先介绍下: -sps [文件路径] 解析 sps 数据 文件当中的内容就是纯方本的hexstring: 如 42 E0 33 8D 68 05 ...
- LeetCode Remove Linked List Elements 删除链表元素
题意:移除链表中元素值为val的全部元素. 思路:算法复杂度肯定是O(n),那么就在追求更少代码和更少额外操作.我做不出来. /** * Definition for singly-linked li ...
- js使用my97插件显示当前时间,且select控制计算时间差
做页面需要两个时间输入框一个显示当前时间,一个显示之前的时间,并且需要一个select下拉框控制两个时间输入框之间的差,效果如下图: 这里使用的是My97DatePicer,简单方便,引入my97插件 ...
- 使Win10用户获得特殊权限以便删除相应文件(夹)
依次访问: 本地用户和组(右击“此电脑”): 用户: 右击:当前用户名: 属性: 添加: 输入:System Managed Accounts Group: 检查名称(可选): 确定: 重启电脑. 参 ...