Linux字符設(shè)備驅(qū)動(dòng)開(kāi)發(fā)基礎(chǔ)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
一. 概念介紹 一般用戶在應(yīng)用程序里調(diào)用的 open, read, write 函數(shù)是 c 庫(kù)的函數(shù), 這些函數(shù)會(huì)觸發(fā) swi val異常,從而引發(fā)系統(tǒng)調(diào)用,進(jìn)入到內(nèi)核空間, 內(nèi)核通過(guò)VFS(virtual Filesystem)來(lái)實(shí)現(xiàn)調(diào)用不同的驅(qū)動(dòng)函數(shù)。 例如:我們有一個(gè)函數(shù),Linux驅(qū)動(dòng):封裝對(duì)底層硬件的操作,向上層應(yīng)用提供操作接口
int main()
{
int fd1, fd2;
int val = 1;
fd1 = open("/dev/led", O_RDWR);
write(fd1, &val, 4);
fd2 = open("hello.txt", O_RDWR);
write(fd2, &val, 4);
}
函數(shù)里相同的open、write函數(shù),引發(fā)的不同的行為,一個(gè)是操控硬件,一個(gè)是寫(xiě)文件。
簡(jiǎn)單的調(diào)用關(guān)系如下:
用戶 –> 系統(tǒng)調(diào)用 –> 驅(qū)動(dòng)程序
open –> sys.open –> led.open
write –> sys.write –> led.write
二. 字符設(shè)備驅(qū)動(dòng)框架
實(shí)現(xiàn)步驟:
實(shí)現(xiàn)驅(qū)動(dòng)的 led.open, led.write, led.read 操作
定義file_operations結(jié)構(gòu)體, 把驅(qū)動(dòng)函數(shù)填充到里面
把這個(gè)結(jié)構(gòu)告訴內(nèi)核, 通個(gè)注冊(cè)函數(shù) register_chrdev(major, “first_drv”, &first_drv_fops) 來(lái)實(shí)現(xiàn)
誰(shuí)來(lái)調(diào)用注冊(cè)函數(shù) –>驅(qū)動(dòng)的入口函數(shù)來(lái)調(diào)用這個(gè)注冊(cè)函數(shù), first_drv_init
修飾一下這個(gè)函數(shù)入口函數(shù),module_init(first_drv_init)
//第一步:驅(qū)動(dòng)功能實(shí)現(xiàn)
static int first_drv_open(struct inode *inode,struct file *file)
{
printk("first_drv_openn");
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos)
{
printk("first_drv_writen");
return 0;
}
//第二步:定義結(jié)構(gòu)體,并把驅(qū)動(dòng)函數(shù)填充進(jìn)去
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */
.open = first_drv_open,
.write = first_drv_write,
};
//第四步:實(shí)現(xiàn)驅(qū)動(dòng)入口函數(shù),來(lái)調(diào)用注冊(cè)函數(shù)
int major;
static int first_drv_init(void)
{
//第三步:通過(guò)使用注冊(cè)函數(shù),把結(jié)構(gòu)體告訴內(nèi)核
major = register_chrdev(0,"first_drv",&first_drv_fops);// 注冊(cè),告訴內(nèi)核
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");//卸載
}
//第五步:修飾入口函數(shù),及退出函數(shù)
module_init(first_drv_init);
module_exit(first_drv_exit);
三. 關(guān)聯(lián) [設(shè)備號(hào)] 與 [設(shè)備節(jié)點(diǎn)]
設(shè)備號(hào)要與設(shè)備結(jié)點(diǎn)關(guān)聯(lián)起來(lái),才能通過(guò)open(“/dev/xyz”)方便的操作。
1. 設(shè)置主設(shè)備號(hào)
驅(qū)動(dòng)程序可以自動(dòng)分配主設(shè)備號(hào), 也可以手工指定
// 設(shè)置為 0 時(shí)是系統(tǒng)自動(dòng)分配主設(shè)備號(hào)
major = register_chrdev(0, "first_drv", &first_drv_fops);
// 通過(guò) [cat /proc/devices] 看一下系統(tǒng)為我們的first_drv分配的設(shè)備號(hào)是多少
// 手動(dòng)分配 666主設(shè)備號(hào)給 first_drv
register_chrdev(666, "first_drv", &first_drv_fops);
2. 設(shè)置設(shè)備節(jié)點(diǎn)
當(dāng)應(yīng)該程序 執(zhí)行 open(“/dev/xyz”) 操作時(shí),這個(gè)/dev/xyz怎么來(lái)的
2.1 手動(dòng)創(chuàng)建
// 創(chuàng)建設(shè)備節(jié)點(diǎn)
mknod /dev/xyz c(表示是字符設(shè)備) 主設(shè)備號(hào) 次設(shè)備號(hào)
//查看設(shè)備信息:
ls -l /dev/xyz
2.2 自動(dòng)創(chuàng)建
mdev – 根據(jù)系統(tǒng)信息創(chuàng)建設(shè)備節(jié)點(diǎn)
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
int major;
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops);
//創(chuàng)建設(shè)備信息,執(zhí)行后會(huì)出現(xiàn) /sys/class/firstdrv
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
//創(chuàng)建設(shè)備節(jié)點(diǎn),就是根據(jù)上面的設(shè)備信息來(lái)的
firstdrv_class_dev = class_device_create(firstdrv_class,
NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
//刪除節(jié)點(diǎn)及信息
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
}
編譯測(cè)試驅(qū)動(dòng):
驅(qū)動(dòng)程序:first_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstdrv_class;
static struct class_device *firstdrv_class_dev;
static int first_drv_open(struct inode *inode,struct file *file)
{
printk("first_drv_openn");
return 0;
}
static ssize_t first_drv_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos)
{
printk("first_drv_writen");
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
major = register_chrdev(0,"first_drv",&first_drv_fops);// 注冊(cè),告訴內(nèi)核
//printk("first_drv_initn");
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");//卸載
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
驅(qū)動(dòng)測(cè)試程序:
#include
#include
#include
#include
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz",O_RDWR);
if(fd < 0)
{
printf("can't open!n");
}
write(fd,&val,4);
return 0;
}
Makefile:
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += first_drv.o
測(cè)試步驟:
①u(mài)boot中掛載文件系統(tǒng)設(shè)置:
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
bootargs=noinitrd root=/dev/nfs nfsroot=192.168.2.3:/work/nfs_root/czg ip=192.168.2.5:192.168.2.3:192.168.2.1:255.255.255.0::eth0:off rootfstype=jffs2 init=/linuxrc console=ttySAC0
②將驅(qū)動(dòng)程序和驅(qū)動(dòng)測(cè)試程序編譯好,并拷貝至NFS文件夾
make
cp first_drv.ko /work/nfs_root/czg
arm-linux-gcc -o firstdrvtest firstdrvtest.c
cp firstdrvtest /work/nfs_root/czg
③加載驅(qū)動(dòng)程序,并測(cè)試
insmod first_drv.ko //卸載:rmmod 查看:lsmod
//測(cè)試
./firstdrvtest