一. 概念介紹 一般用戶在應用程序里調用的 open, read, write 函數是 c 庫的函數, 這些函數會觸發(fā) swi val異常,從而引發(fā)系統(tǒng)調用,進入到內核空間, 內核通過VFS(virtual Filesystem)來實現調用不同的驅動函數。 例如:我們有一個函數,Linux驅動:封裝對底層硬件的操作,向上層應用提供操作接口
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);
}
函數里相同的open、write函數,引發(fā)的不同的行為,一個是操控硬件,一個是寫文件。
簡單的調用關系如下:
用戶 –> 系統(tǒng)調用 –> 驅動程序
open –> sys.open –> led.open
write –> sys.write –> led.write
二. 字符設備驅動框架
實現步驟:
實現驅動的 led.open, led.write, led.read 操作
定義file_operations結構體, 把驅動函數填充到里面
把這個結構告訴內核, 通個注冊函數 register_chrdev(major, “first_drv”, &first_drv_fops) 來實現
誰來調用注冊函數 –>驅動的入口函數來調用這個注冊函數, first_drv_init
修飾一下這個函數入口函數,module_init(first_drv_init)
//第一步:驅動功能實現
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, /* 這是一個宏,推向編譯模塊時自動創(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);// 注冊,告訴內核
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major,"first_drv");//卸載
}
//第五步:修飾入口函數,及退出函數
module_init(first_drv_init);
module_exit(first_drv_exit);
三. 關聯 [設備號] 與 [設備節(jié)點]
設備號要與設備結點關聯起來,才能通過open(“/dev/xyz”)方便的操作。
1. 設置主設備號
驅動程序可以自動分配主設備號, 也可以手工指定
// 設置為 0 時是系統(tǒng)自動分配主設備號
major = register_chrdev(0, "first_drv", &first_drv_fops);
// 通過 [cat /proc/devices] 看一下系統(tǒng)為我們的first_drv分配的設備號是多少
// 手動分配 666主設備號給 first_drv
register_chrdev(666, "first_drv", &first_drv_fops);
2. 設置設備節(jié)點
當應該程序 執(zhí)行 open(“/dev/xyz”) 操作時,這個/dev/xyz怎么來的
2.1 手動創(chuàng)建
// 創(chuàng)建設備節(jié)點
mknod /dev/xyz c(表示是字符設備) 主設備號 次設備號
//查看設備信息:
ls -l /dev/xyz
2.2 自動創(chuàng)建
mdev – 根據系統(tǒng)信息創(chuàng)建設備節(jié)點
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)建設備信息,執(zhí)行后會出現 /sys/class/firstdrv
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
//創(chuàng)建設備節(jié)點,就是根據上面的設備信息來的
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é)點及信息
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
}
編譯測試驅動:
驅動程序: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, /* 這是一個宏,推向編譯模塊時自動創(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);// 注冊,告訴內核
//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");
驅動測試程序:
#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
測試步驟:
①uboot中掛載文件系統(tǒng)設置:
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
②將驅動程序和驅動測試程序編譯好,并拷貝至NFS文件夾
make
cp first_drv.ko /work/nfs_root/czg
arm-linux-gcc -o firstdrvtest firstdrvtest.c
cp firstdrvtest /work/nfs_root/czg
③加載驅動程序,并測試
insmod first_drv.ko //卸載:rmmod 查看:lsmod
//測試
./firstdrvtest