赞
踩
作者用的是正点原子linux阿尔法开发板emmc版本
(1)字符设备就是一个一个字节,按照字节流进行读写操作的设备 驱动的加载与卸载: module_init(); module_exit(); 字符设备的注册与注销: static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) static inline void unregister_chrdev(unsigned int major, const char *name) 首先驱动编写需要有init和exit函数 在init函数中调用register_chrdev注册字符设备 在exit函数中调用unregister_chrdev取消注册字符设备 然后需要实现file_operations这个结构体以及其中的函数,最基本的 owner open read write release写完代码之后,需要写一个makefile来完成模块编译 1 KERNELDIR := /home/zhenghan/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga 2 3 CURRENT_PATH := $(shell pwd) 4 5 obj-m = chardevbase.o 6 7 build: kernel_modules 8 9 kernel_modules: 10 $(MAKE) -C ${KERNELDIR} M=$(CURRENT_PATH) modules 11 clean: 12 $(MAKE) -C ${KERNELDIR} M=$(CURRENT_PATH) clean 编译好的ko拷贝到单板/lib/module/4.1.15下 在单板上insmod xxx.ko xxx是你编译的驱动 加载驱动后,需要手动创建设备节点 makenod /dev/name c 200 0 name为你驱动的名字 c为设备类型 200 为主设备号 0 为次设备号 (2)接下来我们尝试点亮LED灯 首先找到LED灯所连接的引脚,还要了解MMU以及操作mmu的函数 地址映射 void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) void iounmap (volatile void __iomem *addr) 内存访问 1 u8 readb(const volatile void __iomem *addr) 2 u16 readw(const volatile void __iomem *addr) 3 u32 readl(const volatile void __iomem *addr) 1 void writeb(u8 value, volatile void __iomem *addr) 2 void writew(u16 value, volatile void __iomem *addr) 3 void writel(u32 value, volatile void __iomem *addr) 使能时钟 设置gpio引脚的复用 设置gpio引脚的电气属性 压摆率 速度 驱动能力 开漏 上下拉 配置gpio输入输出
(1)设备节点 节点格式:label:node-name@unit-address 可以直接&label来访问这个节点 (2)常用的几种数据形式 字符串 compatible = "arm-cotex-a7"; 32位无符号整数reg = <0>; reg = <0 0x123456 100> 字符串列表 compatible = "fsl","nand"; (3)model属性 model = "wm8960-audio"; 描述设备模块信息 (4)status属性 描述设备状态: "okay" 设备是可操作的 "disable" 设备是不可操作的 "fail" 设备不可操作检测到错误 "fail-sss" 设备不可操作出现了sss错误 (5)#address-cells 、 #size-cells #address-cells:地址信息所占用的字长 #size-cells:长度信息所占用的字长 (6)reg属性 描述设备地址空间资源信息 (7)ranges属性 可以为空 按照(child—bus-address,parent-bus-address,length)格式的数据矩阵 (8)特殊节点 aliases子节点:别名 chosen子节点:用于uboot传递参数给linux
(1)修改dts,添加alphaled节点 alphaled { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-led"; status = "okay"; reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */ 0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */ 0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */ 0X0209C000 0X04 /* GPIO1_DR_BASE */ 0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */ }; (2)写led驱动程序 1.init函数 获取设备节点 of_find_node_by_path 获取compatible of_find_property 获取status of_property_read_string 获取reg of_property_read_u32_array 寄存器映射 of_iomap 初始化led 申请设备号 register_chrdev_region alloc_chrdev_region 注册cdev cdev_init cdev_add 创建mdev (创建类,创建设备)device_create 2.exit函数 取消寄存器映射 iounmap 销毁设备 device_distroy 销毁类 class_distroy 删除cdev cdev_del 注销设备号 unregister_chrdev_region 3.write函数 操作硬件
pinctrl子系统的主要工作内容 1、获取设备树中的pin信息 2、根据获取到的pin信息来设置pin的复用功能 3、根据获取到的pin信息来设置pin的电器特性,比如上下拉、速度、驱动能力等 只需要在设备树里设置好某个pin的相关属性即可,其他的初始化交给pinctrl子系统完成,pinctrl子系统源码/driver/pinctrl #define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0 <mux_reg conf_reg input_reg mux_mode input_val> conf_val gpio子系统API函数 申请gpio管脚 gpio_request(unsigned gpio, const char *label); 释放gpio gpio_free(unsigned gpio); 设置gpio为输入 gpio_direction_input(unsigned gpio); 设置gpio为输出 gpio_direction_output(unsigned gpio,int value); 获取gpio的值 gpio_get_value(unsigned gpio); 设置gpio的值 gpio_set_value(unsigned gpio, int value); 与gpio相关的OF函数 用于获取设备树中某个属性里定义了几个GPIO信息 of_gpio_named_count(struct device_node *np,const char *propname); 统计gpios这个属性的GPIO数量 of_gpio_count(struct device_node *np); 获取GPIO编号 of_get_named_gpio(struct device_node *np, const char *propname, int index); 使用pinctrl子系统和gpio子系统驱动led (1)添加dts节点 找到iomux添加pinctrl节点 在/节点下添加设备节点 (2)写驱动程序 重要的是获取设备节点和获取gpio编号,有了gpio编号才能操作gpio
1.原子整形操作API typedef struct { int counter; } atomic_t; ATOMIC_INIT(int i) 定义原子变量的时候对其初始化。 int atomic_read(atomic_t *v) 读取 v 的值,并且返回。 void atomic_set(atomic_t *v, int i) 向 v 写入 i 值。 void atomic_add(int i, atomic_t *v) 给 v 加上 i 值。 void atomic_sub(int i, atomic_t *v) 从 v 减去 i 值。 void atomic_inc(atomic_t *v) 给 v 加 1,也就是自增。 void atomic_dec(atomic_t *v) 从 v 减 1,也就是自减 int atomic_dec_return(atomic_t *v) 从 v 减 1,并且返回 v 的值。 int atomic_inc_return(atomic_t *v) 给 v 加 1,并且返回 v 的值。 int atomic_sub_and_test(int i, atomic_t *v) 从 v 减 i,如果结果为 0 就返回真,否则返回假 int atomic_dec_and_test(atomic_t *v) 从 v 减 1,如果结果为 0 就返回真,否则返回假 int atomic_inc_and_test(atomic_t *v) 给 v 加 1,如果结果为 0 就返回真,否则返回假 int atomic_add_negative(int i, atomic_t *v) 给 v 加 i,如果结果为负就返回真,否则返回假 2.原子位操作API void set_bit(int nr, void *p) 将 p 地址的第 nr 位置 1。 void clear_bit(int nr,void *p) 将 p 地址的第 nr 位清零。 void change_bit(int nr, void *p) 将 p 地址的第 nr 位进行翻转。 int test_bit(int nr, void *p) 获取 p 地址的第 nr 位的值。 int test_and_set_bit(int nr, void *p) 将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。 int test_and_clear_bit(int nr, void *p) 将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。 int test_and_change_bit(int nr, void *p) 将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。 3.自旋锁 会不停的获取资源,直到资源可用,浪费处理器时间,降低系统性能 typedef struct spinlock { union { struct raw_spinlock rlock; #ifdef CONFIG_DEBUG_LOCK_ALLOC # define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) struct { u8 __padding[LOCK_PADSIZE]; struct lockdep_map dep_map; }; #endif }; } spinlock_t; DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化一个自选变量。 int spin_lock_init(spinlock_t *lock) 初始化自旋锁。 void spin_lock(spinlock_t *lock) 获取指定的自旋锁,也叫做加锁。 void spin_unlock(spinlock_t *lock) 释放指定的自旋锁。 int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0 int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就 返回非 0,否则返回 0 获取锁之前关闭本地中断相关api void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁。 void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁。 void spin_lock_irqsave(spinlock_t *lock, unsigned long flags) 保存中断状态,禁止本地中断,并获取自旋锁。 void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地中断, 释放自旋锁 下半部使用自旋锁api void spin_lock_bh(spinlock_t *lock) 关闭下半部,并获取自旋锁。 void spin_unlock_bh(spinlock_t *lock) 打开下半部,并释放自旋锁。 4.读写自旋锁 同一时刻只能一个线程持有写锁,其他程序不能读,同一时刻可以多个线程持有读锁,其他线程不能写 typedef struct { arch_rwlock_t raw_lock; } rwlock_t; DEFINE_RWLOCK(rwlock_t lock) 定义并初始化读写锁 void rwlock_init(rwlock_t *lock) 初始化读写锁。 读锁 void read_lock(rwlock_t *lock) 获取读锁。 void read_unlock(rwlock_t *lock) 释放读锁。 void read_lock_irq(rwlock_t *lock) 禁止本地中断,并且获取读锁。 void read_unlock_irq(rwlock_t *lock) 打开本地中断,并且释放读锁。 void read_lock_irqsave(rwlock_t *lock, unsigned long flags) 保存中断状态,禁止本地中断,并获取读锁。 void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地 中断,释放读锁。 void read_lock_bh(rwlock_t *lock) 关闭下半部,并获取读锁。 void read_unlock_bh(rwlock_t *lock) 打开下半部,并释放读锁。 写锁 void write_lock(rwlock_t *lock) 获取写锁。 void write_unlock(rwlock_t *lock) 释放写锁。 void write_lock_irq(rwlock_t *lock) 禁止本地中断,并且获取写锁。 void write_unlock_irq(rwlock_t *lock) 打开本地中断,并且释放写锁。 void write_lock_irqsave(rwlock_t *lock, unsigned long flags) 保存中断状态,禁止本地中断,并获取写锁。 void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地 中断,释放读锁。 void write_lock_bh(rwlock_t *lock) 关闭下半部,并获取读锁。 void write_unlock_bh(rwlock_t *lock) 打开下半部,并释放读锁。 5.顺序锁 允许在写的时候进行读操作,不允许同时进行并发写操作 typedef struct { struct seqcount seqcount; spinlock_t lock; } seqlock_t; DEFINE_SEQLOCK(seqlock_t sl) 定义并初始化顺序锁 void seqlock_ini seqlock_t *sl) 初始化顺序锁。 顺序锁写操作 void write_seqlock(seqlock_t *sl) 获取写顺序锁。 void write_sequnlock(seqlock_t *sl) 释放写顺序锁。 void write_seqlock_irq(seqlock_t *sl) 禁止本地中断,并且获取写顺序锁 void write_sequnlock_irq(seqlock_t *sl) 打开本地中断,并且释放写顺序锁。 void write_seqlock_irqsave(seqlock_t *sl, unsigned long flags) 保存中断状态,禁止本地中断,并获取写顺序 锁。 void write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) 将中断状态恢复到以前的状态,并且激活本地 中断,释放写顺序锁。 void write_seqlock_bh(seqlock_t *sl) 关闭下半部,并获取写读锁。 void write_sequnlock_bh(seqlock_t *sl) 打开下半部,并释放写读锁。 顺序锁读操作 unsigned read_seqbegin(const seqlock_t *sl) 读单元访问共享资源的时候调用此函数,此函 数会返回顺序锁的顺序号。 unsigned read_seqretry(const seqlock_t *sl, unsigned start) 读结束以后调用此函数检查在读的过程中有 没有对资源进行写操作,如果有的话就要重读
同步的一种方式,适用于哪些占用资源比较久的场合
信号量api
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
DEFINE_SEAMPHORE(name) 定义一个信号量,并且设置信号量的值为 1。
void sema_init(struct semaphore *sem, int val) 初始化信号量 sem,设置信号量值为 val。
void down(struct semaphore *sem)
获取信号量,因为会导致休眠,因此不能在中断中使用。
int down_trylock(struct semaphore *sem);
尝试获取信号量,如果能获取到信号量就获取,并且返回 0。如果不能就返回非 0,并且不会进入休眠。
int down_interruptible(struct semaphore *sem)
获取信号量,和 down 类似,只是使用 down 进入休眠状态的线程不能被信号打断。而使用此函数进入休眠以后是可以被信号打断的。
void up(struct semaphore *sem) 释放信号量
struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count; spinlock_t wait_lock; }; DEFINE_MUTEX(name) 定义并初始化一个 mutex 变量。 void mutex_init(mutex *lock) 初始化 mutex。 void mutex_lock(struct mutex *lock) 获取 mutex,也就是给 mutex 上锁。如果获 取不到就进休眠。 void mutex_unlock(struct mutex *lock) 释放 mutex,也就给 mutex 解锁。 int mutex_trylock(struct mutex *lock) 尝试获取 mutex,如果成功就返回 1,如果失 败就返回 0。 int mutex_is_locked(struct mutex *lock) 判断 mutex 是否被获取,如果是的话就返回 1,否则返回 0。 int mutex_lock_interruptible(struct mutex *lock) 使用此函数获取信号量失败进入休眠以后可 以被信号打断。
include/asm-generic/param.h
# define HZ CONFIG_HZ 系统频率
处理32位jiffies绕回 time_after(unkown, known) time_before(unkown, known) unkown 通常为 jiffies, known 通常是需要对比的值。 time_after_eq(unkown, known) time_before_eq(unkown, known) jiffies和ms、us、ns之间的转换函数 int jiffies_to_msecs(const unsigned long j) 将 jiffies 类型的参数 j 分别转换为对应的毫秒、 int jiffies_to_usecs(const unsigned long j) 微秒、纳秒。 u64 jiffies_to_nsecs(const unsigned long j) long msecs_to_jiffies(const unsigned int m) long usecs_to_jiffies(const unsigned int u) 将毫秒、微秒、纳秒转换为 jiffies 类型。 unsigned long nsecs_to_jiffies(u64 n) timer_list结构体表示内核定时器 struct timer_list { struct list_head entry; unsigned long expires; /* 定时器超时时间,单位是节拍数 */ struct tvec_base *base; void (*function)(unsigned long); /* 定时处理函数 */ unsigned long data; /* 要传递给 function 函数的参数 */ int slack; }; void init_timer(struct timer_list *timer) 初始化定时器 void add_timer(struct timer_list *timer) 添加一个定时器并开始运行 int del_timer(struct timer_list * timer) 删除定时器 int del_timer_sync(struct timer_list *timer) 等待定时器用完在删除 int mod_timer(struct timer_list *timer, unsigned long expires) 修改定时值 内核短延时函数 void ndelay(unsigned long nsecs) void udelay(unsigned long usecs) 纳秒、微秒和毫秒延时函数。 void mdelay(unsigned long mseces)
1、中断号 每个中断都有一个中断号,通过中断号区分不同的中断 2、request_irq函数 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) irq:要申请中断的中断号。 handler:中断处理函数,当中断发生以后就会执行此中断处理函数。 flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志, name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。 dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将 dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。 返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经 被申请了 这里我们介绍几个常用的中断标志 IRQF_SHARED 多个设备共享一个中断线,共享的所有中断都必须指定此标志。 如果使用共享中断的话, request_irq 函数的 dev 参数就是唯一 区分他们的标志。 IRQF_ONESHOT 单次中断,中断执行一次就结束。 IRQF_TRIGGER_NONE 无触发。 IRQF_TRIGGER_RISING 上升沿触发。 IRQF_TRIGGER_FALLING 下降沿触发。 IRQF_TRIGGER_HIGH 高电平触发。 IRQF_TRIGGER_LOW 低电平触发。 3、free_irq函数 使用完成以后,要释放中断 void free_irq(unsigned int irq, void *dev) irq: 要释放的中断。 dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。 4、中断处理函数 irqreturn_t (*irq_handler_t) (int, void *) 第一个参数是要中断处理函数要相应的中断号。第二个参数是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。 中断处理函数的返回值为 irqreturn_t 类型 enum irqreturn { IRQ_NONE = (0 << 0), IRQ_HANDLED = (1 << 0), IRQ_WAKE_THREAD = (1 << 1), }; typedef enum irqreturn irqreturn_t; 一般中断服务函数返回值使用如下形式 return IRQ_RETVAL(IRQ_HANDLED) 5、中断使能与禁止 void enable_irq(unsigned int irq) void disable_irq(unsigned int irq) 等待中断执行结束才返回 void disable_irq_nosync(unsigned int irq) 立即返回 关闭全局中断,这个时候可以使用如下两个函数: local_irq_enable() local_irq_disable() 禁止和恢复中断 local_irq_save(flags) local_irq_restore(flags) 6、上半部与下半部 上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。 下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。 下半部机制: 软中断 注册软中断void open_softirq(int nr, void (*action)(struct softirq_action *)) 触发软中断void raise_softirq(unsigned int nr) tasklet: struct tasklet_struct { struct tasklet_struct *next; /* 下一个 tasklet */ unsigned long state; /* tasklet 状态 */ atomic_t count; /* 计数器,记录对 tasklet 的引用数 */ void (*func)(unsigned long); /* tasklet 执行的函数 */ unsigned long data; /* 函数 func 的参数 */ }; 初始化tasklet void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data); DECLARE_TASKLET(name, func, data) 在中断上半部调用tasklet void tasklet_schedule(struct tasklet_struct *t) 7、工作队列 struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; /* 工作队列处理函数 */ }; 工作队列初始化 #define INIT_WORK(_work, _func) #define DECLARE_WORK(n, f) 一次性完成创建和初始化 调用工作队列 bool schedule_work(struct work_struct *work) 8、从dts获取中断号 unsigned int irq_of_parse_and_map(struct device_node *dev, int index) 获取gpio对应的中断号 int gpio_to_irq(unsigned int gpio)
1、阻塞IO方式访问
当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,那么阻塞式IO就会将应用程序对应的线程挂起,直到设备资源可以获取
2、非阻塞IO方式访问
当应用程序对设备驱动进行操作的时候,如果不能获取到设备资源,程序不会挂起,要么一直轮询等待,要么直接放弃
3、等待队列
阻塞访问最大的好处就是当设备文件不可操作时,进程可以进入休眠态、可以将cpu资源让出来,当设备可以操作时必须唤醒进程,一般在中断函数中完成唤醒工作。 在驱动中使用等待队列,需要创建并初始化一个等待队列头 struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t; 初始化等待队列头 void init_waitqueue_head(wait_queue_head_t *q) 也可以一次性初始化 DECLARE_WAIT_QUEUE_HEAD() 等待队列项 struct __wait_queue { unsigned int flags; void *private; wait_queue_func_t func; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t; 也可以使用宏定义并初始化一个等待队列项 DECLARE_WAITQUEUE(name, tsk) name:等待队列项的名字 tsk:一般为current 将队列项添加\移除 等待队列头 void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait) 等待唤醒 (主动唤醒) void wake_up(wait_queue_head_t *q) void wake_up_interruptible(wait_queue_head_t *q) 等待事件(满足条件唤醒) wait_event(wq, condition) wait_event_timeout(wq, condition, timeout) wait_event_interruptible(wq, condition) wait_event_interruptible_timeout(wq,condition, timeout) 与 wait_event_timeout 函数类似,此函数也将进程设置为 TASK_INTERRUPTIBLE,可以被信号打断
4、轮询(非阻塞)
1、select int select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) nfds:最大文件描述符+1 readfds:读集合 writefds:写集合 exceptfds:异常 timeout:超时 void FD_ZERO(fd_set *set) void FD_SET(int fd, fd_set *set) void FD_CLR(int fd, fd_set *set) int FD_ISSET(int fd, fd_set *set)
2、poll int poll( struct pollfd *fds, nfds_t nfds, int timeout ) fds:要监视的文件描述符集合以及要监视的事件 struct pollfd { int fd; /* 文件描述符 */ short events; /* 请求的事件 */ short revents; /* 返回的事件 */ }; events: POLLIN 有数据可以读取。 POLLPRI 有紧急的数据需要读取。 POLLOUT 可以写数据。 POLLERR 指定的文件描述符发生错误。 POLLHUP 指定的文件描述符挂起。 POLLNVAL 无效的请求。 POLLRDNORM 等同于 POLLIN nfds:要监视的文件描述符数量 timeout :超时时间
3、epoll int epoll_create(int size) int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) op:要进行的操作 EPOLL_CTL_ADD 向 epfd 添加文件参数 fd 表示的描述符。 EPOLL_CTL_MOD 修改参数 fd 的 event 事件。 EPOLL_CTL_DEL 从 epfd 中删除 fd 描述符。 fd:要监视的文件描述符 event:要监视的时间类型 struct epoll_event { uint32_t events; /* epoll 事件 */ epoll_data_t data; /* 用户数据 */ }; events : EPOLLIN 有数据可以读取。 EPOLLOUT 可以写数据。 EPOLLPRI 有紧急的数据需要读取。 EPOLLERR 指定的文件描述符发生错误。 EPOLLHUP 指定的文件描述符挂起。 EPOLLET 设置 epoll 为边沿触发,默认触发模式为水平触发。 EPOLLONESHOT 一次性的监视,当监视完成以后还需要再次监视某个 fd,那么就需要将fd 重新添加到 epoll 里面。 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
驱动开发接口汇总
字符设备的注册与注销: static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) static inline void unregister_chrdev(unsigned int major, const char *name) 内存访问 1 u8 readb(const volatile void __iomem *addr) 2 u16 readw(const volatile void __iomem *addr) 3 u32 readl(const volatile void __iomem *addr) 1 void writeb(u8 value, volatile void __iomem *addr) 2 void writew(u16 value, volatile void __iomem *addr) 3 void writel(u32 value, volatile void __iomem *addr) 地址映射与取消映射 ioremap(cookie,size) void iounmap (volatile void __iomem *addr) 申请与注销设备号 没有设备号:extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); 给定设备号:extern int register_chrdev_region(dev_t, unsigned, const char *); extern void unregister_chrdev_region(dev_t, unsigned); cdev的注册与注销 struct cdev test_cdev; void cdev_init(struct cdev *, const struct file_operations *); int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *); 类的创建与销毁 struct class *class_create(owner, name); 设备的创建与销毁 struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...); extern void device_destroy(struct class *cls, dev_t devt); extern void class_destroy(struct class *cls); 获取设备树中的节点 static inline struct device_node *of_find_node_by_path(const char *path) 获取compatible属性内容 static inline struct property *of_find_property(const struct device_node *np, const char *name, int *lenp) 获取status属性内容 static inline int of_property_read_string(struct device_node *np, const char *propname, const char **out_string) 获取reg属性内容 static inline int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。