Linux内核常用API

参考书籍:嵌入式Linux驱动开发教程(华清远见 姜先刚 刘洪涛)

1.模块驱动
1.1 module
module_init(xxx)
module_exit(xxx)
MODULE_LICENSE(“xxx”)
MODULE_AUTHOR(“xxx”)
MODULE_DESCRIPTION(“xxx”)
MODULE_ALIAS(“xxx”)
1.2 module param
module_param(baudrate,int,S_IRUGO)
1.3 module dependency
EXPORT_SYMBOL(printk)

2.字符设备驱动(虚拟串口)
2.1 character driver
MKDEV(VSER_MAJOR,VSER_MINOR)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
int alloc_chrdev_region(dev_t *dev, unsigned basemior, unsigned count, const char *name)
unregister_chrdev_region(dev_t *dev, unsigned count)
void cdev_init(struct cdev *dev, const struct file_operations *fops)
void cdev_del(struct cdev *p)
struct cdev *cdev_alloc(void)
int (*open)(struct inode *, struct file *)//可被多次打开
int (*release)(struct inode *, struct file *)//全部关闭后执行
2.2 fifo
DEFINE_KFIFO(fifo, type, size)
kfifo_from_user(fifo, from, len, copied)
kfifo_to_user(fifo, to, len, copied)

3.高级IO操作
3.1 ioctl
long (*unlock_ioctl)(struct file *, unsigned int, unsigned long)
#define VS_SEET_FFMT _IOW(VS_MAGIC, 2, struct option)
unsigned long copy_from_user(void * to, const void __user * from, unsigned long n)
unsigned long copy_to_user(void __user * to, const void * from, unsigned long n);
3.2 proc
vsdev.pdir = proc_mkdir(“vser”, NULL)
vsdev.pdat = proc_create_data(“info”, 0, vsdev.pdir, &proc_ops, &vsdev)
remove_proc_entry(“info”, vsdev.pdir)
seq_printf(m, “xxx”)
3.3 wait queue
DECLARE_WAIT_QUEUE_HEAD(name)
init_waitqueue_head(&name)
wait_event_interruptible(wq, condition)
wake_up_interruptible(x)
3.4 poll(多路复用IO,驱动中的poll只是注册唤醒事件,因此属于异步IO)
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
poll_wait(filp, wait_queue, p)
3.5 aio_write aio_read
ssize_t (*aio_read)(struct kiocb *, const struct iovec *, unsigned long, loft_t)
ssize_t (*aio_write)(struct kiocb *, const struct iovec *, unsigned long, loft_t)
3.6 fasync
fasync_helper(fd, filp, on, dev_fasync_struct)
kill_fasync(dev_fasync, SIGIO, PILLIN)
3.7 mmap
int (*mmap)(struct file *filp, struct vm_area_struct *vma)
remap_pfn_range(vma, vma->start, virt_to_phys(buf)>>PAGE_SHIFT, vma->end - vma->start, vma->vm_page_prot)
3.8 llseek
loff_t (llseek)(struct file, loff_t, int)

4.中断与定时
4.1 __irq_user执行说明内核层代码被打断 __irq_svc说明用户层代码被打断
4.2 request_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
void free_irq(unsinged int, void *)
kfifo_in(&fifo, buf, size)
cat /proc/interrupts 可以查看设备中断号
中断处理上下文中不能使用kfifo_to_user,kfifo_from_user,copy_from_user,copy_to_user,wait_event_xxx等引起任务调度的函数,另外中断操作函数如disable_irq可能会引起死锁
4.3 soft_irq
指在irq_handle之后重新开启硬件irq的中断下半部实现,常见有TIMER_SOFTIRQ,NET_TX/RX_SOFTIRQ,TASKLET_SOFTIRQ,HRTIMER_SOFTIRQ
4.4 tasklet (中断下半部实现,回调函数在运行在中断上下文,未处理的tasklet被重新调度将会被忽略)
DECLARE_TASKLET(name, func, data)
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsinged long data)
void tasklet_schedule(struct tasklet_struct *t)
4.5 work (中断下半部实现,运行在进程上下文)
DECLARE_WORK(n, f)
INIT_WORK(_work, _func)
bool schedule_work(struct work_struct *work)
4.6 delay
ndelay() udelay() mdelay()都是内核启动通过系统节拍粗略定义出来的忙等待延时
msleep() ssleep()通过进程休眠达到延时
4.7 timer (最高精度为系统节拍HZ的定时器, 回调函数在运行在中断上下文)
init_timer(timer)
void add_timer(struct timer_list *timer)
int mod_timer(struct timer_list *timer, unsigned long expires)
int del_timer(struct timer_list *timer)
4.8 hrtimer (回调函数在运行在中断上下文)
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum hrtimer_mode mode)
int hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
static inline u64 hrtimer_forward_now(struct hrtimer *timer, ktime_t interval)
int hrtimer_cancle(struct hrtimer *timer)

5 互斥与同步
5.1 屏蔽中断,优选下面两个函数,防止别的任务关闭了中断,本进程退出临界区,错误的打开了中断
local_irq_save(flags) //把当前状态存储起来, 并且关闭中断
local_irq_restore(flags) //打开中断,并且恢复进入前中断状态
5.2 原子变量
atomic_t i = ATOMIC_INIT(5);
atomic_inc(&i)
atomic_add(k, &i)
5.3 自旋锁(忙等待,可用于中断上下文)
spin_lock_init(&lock)
spin_lock_irqsave(&lock, flags) //防止中断自旋锁的忙等待
spin_unlock_irqrestore(&lock, flags)
5.4 读写锁 (write锁可以阻塞read锁)
rw_lock_init(&lock)
write_lock_irqsave(&lock, flags)
write_unlock_irqrestore(&lock, flags)
5.5 顺序锁 (读操作完成后检查期间是否发生写操作,是则重新读取,适合写的少的场景)
seqlock_init(&lock)
write_seqlock_irqsave(&lock, flags)
write_sequnlock_irqstore(&lock, flags)
read_seqbegin(&lock)
read_seqretry(&lock, start)
5.6 信号量 (引起任务调度,不能用于中断上下文)
DEFINE_SEMAPHORE()
sema_init(&sem, 1)
down(&sem)
up(&sem)
5.7 读写信号量(引起任务调度,不能用于中断上下文)
init_rwsem(sem)
void down_write(struct rw_semaphore *sem)
void up_write(struct rw_semaphore *sem)
5.8 互斥量(引起任务调度,不能用于中断上下文)
mutex_init(&lock)
mutex_lock(&lock)
mutex_unlock(&lock)
5.9 RCU(Read-Copy Update)
rcu_read_lock()
ret=rcu_dereference(gbl_foo)->a
rcu_read_unlock()
rcu_assign_pointer(gbl_foo, new_fp)
synchronize_rcu()
5.10 互斥应用
wait_event_interruptible_lock(wq, condition) //调度前释放锁,唤醒再获取锁
kfifo_out(&fifo, buf, size) //不能进行任务调度时,可以替换kfifo_to_user和kfifo_from_user
5.11 完成量(注意每次使用信号量之前都必须重新初始化,因为一旦释放后,该结构就不存在了,这个也是跟信号量的区别)
void init_completion(struct completion *x)
wait_for_completion(struct completion *)
void complete(struct completion *)
void complete_all(struct completion *)

6.内存
6.1 按页分配内存
6.1.1 分配页,返回物理地址
内存可以分为ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE几种,但ARM对DMA访问没有范围限制,因此一般只考虑高端或者非高端区域
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order) 获取2的order次方个页
alloc_page(gfp_mask) 对alloc_pages的封装,order=0
void __free_pages(strcut page *page, unsinged int order)
GFP_ATOMIC 防止发生进程上下文调度,常用于中断和自旋锁上下文
GPF_KERNEL 从常用内存分配,有线从ZONE_NORMAL区域分配
GFP_USER 为用户空间分配内存页
GFP_DMA 优先从ZONE_DMA区分配内存,对于ARM,内存区域没有限制
__GFP_HIGHMEM 有线从ZONE_HIGHMEM区域分配内存
6.1.2 将上述物理地址映射成虚拟地址
void *page_address(const struct page *page) 仅支持ZONE_NORMAL线性映射区地址页
void *kmap(struct page *page) 支持ZONE_HIGHMEM的页
void *kmap_atomic(struct page *page)
void kunmap(struct page *page) 解除kmap的映射
6.1.2 直接获取页并且返回虚拟地址
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
unsigned long __get_free_page(gfp_t gfp_mask) 获取单个页
unsigned long get_zeroed_page(gfp_t gfp_mask) 对返回的内存页清零
void free_pages(unsigend long addr, unsigend int order);
void free_page(unsigned long addr)
6.2 通过slab分配内存(内核预留了若干页,方便用户申请少字节内存)
6.2.1 确定每次分配固定大小的结构体
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
void kmem_cache_free(struct kmem_cache *cacheq, void *objp)
void kmem_cache_destroy(struct kmem_cache *cacheq)
6.2.2 内核中已经预留了若干常用字节数的高速缓存(cat /proc/slabinfo | grep kmalloc)
void *kmalloc(size_t size, gfp_t flags)
void *kmalloc(size_t size, gfp_t flags)
void kfree(const void *)
6.3 物理不连续的内存申请
void *vmalloc(unsigned long size)
void *vzalloc(unsigned long size)
void vfree(const void *addr)
6.4 per-CPU变量
DEFINE_PER_CPU(type, name)
DECLARE_PER_CPU(type, name)
get_cpu_var(var)
put_cpu_var(var)
per_cpu(var, cpu)
alloc_percpu(tpye)
void free_percpu(void __percpu *__pdata)
for_each_possible_cpu(cpu)
6.5 I/O内存
request_mem_region(start,n,name) 向内核申请io内存的独有权,当地址被多个驱动使用不可用
release_mem_region(start,n)
void __iomem *ioremap(phys_addr_t offset, unsigned long size)
void iounmap(void __iomem *addr)
u8 readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)
void writeb(u8 b, volatile void __iomem *addr)
void writew(u16 b, volatile void __iomem *addr)
void write1(u32 b, volatile void __iomem *addr)
ioread8(addr)
ioread16(addr)
ioread32(addr)
iowrite8(v, addr)
iowrite16(v, addr)
iowrite32(v, addr)
ioread8_rep(p, dst, count)
ioread16_rep(p, dst, count)
ioread32_rep(p, dst, count)
iowrite8_rep(p, dst, count)
iowrite16_rep(p, dst, count)
iowrite32_rep(p, dst, count)

7.DMA
7.1 一致性映射(0xFFC00000~0XFFEFFFFF共3MB专门预留的关闭cache的内存地址)
#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL)
static inline void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs)
7.2 DMA池 专门用于一致性少内存的映射
struct dma_poll *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t boundary)
void *dma_poll_alloc(struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle)
void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma)
void dma_pool_destroy(struct dma_pool *pool)
7.3 流式DMA映射,一般是上层驱动传下来的虚拟地址,上层驱动需要对cache操作,以保持一致性
dma_map_single(d, a,s, r)
dma_unmap_single(d, a,s, r)
7.4 分散/聚集映射,相当于一次启动多次流式DMA映射
int dma_map_sg(struct_device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir)
void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, enmu dma_data_direction dir)
7.5 DMA的一般编程接口
struct dma_chan *dma_request_channel(dma_cap_mask_t mask, dma_filter_fn filter_fn, void *filter_param)
typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param) 一般就是通过name找到对应的dma channel
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
struct dma_async_tx_descriptor *(*chan->device->device_prep_slave_sg)(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags) 用于产生传输描述符
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) 提交传输请求
void dma_async_issue_pending(struct dma_chan *chan) 启动传输

8 设备模型
8.1 sysfs
kset = kset_create_and_add(“kset”, NULL, NULL) 创建keset父节点
kobj1 = kobject_create_and_add(“kobj1”, kset->kobj) 在kset下创建子节点
static ssize_t val_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
static ssize_t val_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
static struct kobj_attribute kobj1_val_attr = __ATTR(val, 0666, val_show, val_store)
static struct attribut *kobj1_attrs[] = {
&kobj1_val_attr.attr,
NULL,
}
static struct attribut_group kobj1_attr_group = {
.attrs = kobj1_attrs,
}
sysfs_create_group(kobj1, &kobj1_attr_group) 为kboj创建属性
sysfs_create_link(kobj2, kobj1, “kobj1”) 创建软连接
8.2 平台设备及其驱动
int platform_add_devices(struct platform_device **devs, int num)
int platform_device_register(struct platform_device *pdev)
void platform_device_unregister(struct platform_device *pdev)
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
resource_size_t resource_size(const struct resource *res)
platform_driver_register(drv) 当module_init和module_exit只有platform driver的注册和删除时,可以使用这个宏进行简化
void platform_driver_unregister(struct platform_driver *)
8.3 udev(mdev)和驱动自动加载与自动创建设备节点
udev(mdev)可以通过监控sysfs文件系统自动为驱动创建设备节点:
class_create(owner, name)
void class_destroy(struct class *cls)
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, …)
void device_destroy(struct class class, dev_t devt)
另外udev(mdev)也可通过监控设备节点的生成,执行shell脚本,比如检测到sd
节点,mount对应的文件系统到指定路径
8.4 设备树
所有的dts设备都会在/sys/devices/platform生成一个目录,其下的of_node可以查看节点属性
设备树解析可以参考/Documentation/devicetree/bindings/或者http://www.devicetree.org/specifications/或者/Documentation/devicetree/usage-modle.txt
设备树数据表现形式:
1.cells:使用尖括号限定,32位无符号整数
2.字符串:使用双引号限定
3.二进制数据:用方括号限定
设备树语法:
/memreserve/

;
[lavbel:]node-name[@unit-address]{
[properties definitions];
[child nodes];
};
对于相同的节点的不同属性信息都会被合并,相同节点的相同的属性会被重写,这个语法可以避免移植者四处找节点,直接在板级.dts增改即可
指定节点需要使用全路径,使用引用比较方便,引用可以通过&alises,&lable和handle值表示
设备树查错,可以将编译出来的dtb转换成dts,查询节点属性最后的取值
/script/dtc/dtc -I dtb -O dts -o tmp.dts xxx.dtb
特殊的属性:
#address-cells:设备节点属性,reg属性使用n个32位整数表示地址,如64位系统n=2
#size-cells:设备节点属性,reg属性使用n个32位整数表示数据长度
每个可编址设备都有一个reg,属性对应platform_device的IORESOURCE_MEM资源,对于上述两个属性,可继承自父节点
range:设备节点属性,完成地址映射,格式为<字地址 父地址 字地址空间区域大小> ,为空表示一样的地址域
interrupt-controller:中断控制节点的专用属性,值为空,表明节点是一个中断控制节点
#interrupt-cells:中断控制节点的专用属性,表示中断指示符cell的个数,类似于#address-cells
interrupt-parent:设备节点属性,节点的中断控制器,可继承自父节点,一般值为一个引用
interrupts:设备节点属性,中断指示符列表
phandle:设备节点属性,作为该设备在其他地方的引用值,使用lable作为节点引用的时候,dtc编译器自动为节点产生phandle值
特殊节点:
chosen:可以用来设置bootargs启动参数
alises:用于指定节点的别名

将内核api添加到man:
sudo apt-get install xmlto
内核源码中执行
make ARCH=arm mandocs
make ARCH=arm installmandocs
使用
man 9 cdev_init

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值