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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章