創建字符設備節點的方法
- mknod /dev/xxx c 252 0
在編寫驅動程序時:
主要作用:1) 指定函數在內存的存放位置
2)在kernel初始化後期,釋放所有這些函數代碼所佔的內存空間
經常在入口函數和出口函數里加上 __init ,__exit,作用如下:
module_init除了初始化加載之外,還有後期釋放內存的作用。linux kernel中有很大一部分代碼是設備驅動代碼,這些驅動代碼都有初始化和反初始化函數,這些代碼一般都只執行一次,爲了有更有效的利用內存,這些代碼所佔用的內存可以釋放出來。
linux就是這樣做的,對只需要初始化運行一次的函數都加上__init屬性,__init 宏告訴編譯器如果這個模塊被編譯到內核則把這個函數放到(.init.text)段,module_exit的參數卸載時同__init類似,如果驅動被編譯進內核,則__exit宏會忽略清理函數,因爲編譯進內核的模塊不需要做清理工作,顯然__init和__exit對動態加載的模塊是無效的,只支持完全編譯進內核。
在kernel初始化後期,釋放所有這些函數代碼所佔的內存空間。連接器把帶__init屬性的函數放在同一個section裏,在用完以後,把整個section釋放掉。當函數初始化完成後這個區域可以被清除掉以節約系統內存。Kenrel啓動時看到的消息“Freeing unused kernel memory: xxxk freed”同它有關。
一般來說,寄存器的地址是物理地址,而在系統中用的都是虛擬地址,而要實現寄存器的物理地址轉化爲虛擬地址:
都要在入口函數調用ioremap函數, 定義如下:
void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
在出口函數調用void iounmap(void * addr);
例:
在入口函數:
static int __init hello_init(void)
{
voliate unsigned long *gpfcon
gpfcon = (voliate ussigned long *)iroeamp(0xbfd010C0,16);
}
在出口函數:
static void __exit hello_exit(void)
{
iounmap(gpfcon);
}
但在loongson開發板上:
而在以mips架構的loongson開發板上的寄存器無需調用ioremap,具體原因是在loongson 開發板實現了寄存器的物理地址等於虛擬地址,,無需轉化,
但即使虛擬地址與物理地址相同,都需要經過MMU調度實現虛擬地址轉化爲物理地址。
在實現用戶與內核之間數據的傳遞時,一般要調用以下兩個函數:
- static inline unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
作用:從用戶區讀取數據到內核區
簡述:
- static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
// 實現用戶與內核值的調用,一般用ioctl函數
ioctl 函數接口語法要點
頭文件:#include <linux/fs.h>
- int(*ioctl)(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg)
inode:文件的內核內部結構指針
filp:被打開的文件描述符
cmd:命令類型
arg:命令相關參數
當在內核編譯模塊時,
WARNING: "register_chrdev_region" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "cdev_add" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "cdev_init" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "alloc_chrdev_region" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "printk" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "unregister_chrdev_region" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
WARNING: "cdev_del" [/home/cam/桌面/hello_drive/hello_drive.ko] undefined!
CC /home/cam/桌面/hello_drive/hello_drive.mod.o
/home/cam/桌面/hello_drive/hello_drive.mod.c:8: error: variable '__this_module' has initializer but incomplete type
/home/cam/桌面/hello_drive/hello_drive.mod.c:9: error: unknown field 'name' specified in initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:9: warning: excess elements in struct initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:9: warning: (near initialization for '__this_module')
/home/cam/桌面/hello_drive/hello_drive.mod.c:10: error: unknown field 'init' specified in initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:10: warning: excess elements in struct initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:10: warning: (near initialization for '__this_module')
/home/cam/桌面/hello_drive/hello_drive.mod.c:14: error: unknown field 'arch' specified in initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:14: error: 'MODULE_ARCH_INIT' undeclared here (not in a function)
/home/cam/桌面/hello_drive/hello_drive.mod.c:14: warning: excess elements in struct initializer
/home/cam/桌面/hello_drive/hello_drive.mod.c:14: warning: (near initialization for '__this_module')
make[2]: *** [/home/cam/桌面/hello_drive/hello_drive.mod.o] 錯誤 1
當出現以下情況時:
error:variable '__this_module' has initializer but incomplete type
error: unknown field 'init' specified in initializer
一般是你在內核中未加入模塊支持選項
解決辦法:make menuconfig 選中enable loadable module suppot選項即可 ,然後重新make
字符設備的生成過程:
1)設備號分配與釋放
int register_chrdev_region (dev_t first, unsigned int count, char *name) // 靜態已有的設備號
int alloc_chrdev_region (dev_t *dev, unsigned int firstminor, unsigned int count, char *name) //動態申請設備號,
void unregister_chrdev_region (dev_t first, unsigned int count) //註銷設備號
2)在獲得了系統分配的設備號之後,通過註冊設備才能實現設備號和驅動程序之間的關聯
各個函數如下:
頭文件:#include <linux/cdev.h>
sturct cdev *cdev_alloc(void)
void cdev_init(struct cdev *cdev, struct file_operations *fops)
int cdev_add (struct cdev *cdev, dev_t num, unsigned int count)
void cdev_del(struct cdev *dev)
在 Linux 內核中使用 struct cdev 結構來描述字符設備,我們在驅動程序中必須將已分配到的設備號以及設備操作接口(即爲 struct file_operations 結構)賦予struct cdev 結構變量。1)首先使用 cdev_alloc()函數向系統申請分配 struct cdev 結構, ( 附註:也可以直接定義 struct cdev 結構 ,而不用調用cdev_alloc)
2)再用 cdev_init()函數初始化已分配到的結構並與 file_operations 結構關聯起來。
3)最後調用 cdev_add()函數將設備號與 struct cdev 結構進行關聯並向內核正式報告新設備的註冊,這樣新設備可以被用起來了。
流程如下:
sturct cdev *cdev_alloc(void)函數->生成struct cdev-> void cdev_init(struct cdev *cdev, struct file_operations *fops)
->int cdev_add (struct cdev *cdev, dev_t num, unsigned int count)
4)如果要從系統中刪除一個設備,則要調用 cdev_del()函數。