一、撰寫前的準備作業
本博客是在虛擬機中撰寫驅動程式,然后交叉編譯到樹莓派,所以需要確認幾件事:
1. 確保樹莓派的內核版本和虛擬機中的Linux內核版本保持一致,否則無法安裝驅動;
2. 虛擬機中有交叉編譯工具;
3. 對于樹莓派4來說,交叉編譯驅動模塊的時候,KERNEL=kernel7l,樹莓派2、3代KERNEL=kernel7;
二、驅動程式撰寫
驅動的撰寫同樣會根據上一篇博客(Linux底層驅動的簡單認知)的框架來寫:
1.構建 file_operations結構體
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
static int Test_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n");
return 0;
}
static ssize_t Test_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
printk("pin4_write\n");
return 0;
}
static struct file_operations pin4_fops ={
.owner=THIS_MODULE,
.open= Test_open,
.write=Test_write,
}
使用static關鍵字是為了函式名沖突,誰都不能保證擁有一萬多C檔案的Linux內核中中會不會有名字沖突,所以 static很有必要,這個程式的功能會在內核環境列印相關資訊,
2.撰寫初始化函式
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>
static struct class *Test_class;
static struct device *Test_dev;
static dev_t devno; //device numble
static int major=240; //major device numble
static int minor=0; //minor device numble
static char *module_name = "test";//device name
static intTest_open(struct inode *inode,struct file *file)
{
printk("Test_open\n");
return 0;
}
static ssize_t Test_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
printk("Test_write\n");
return 0;
}
static struct file_operations Test_fops ={
.owner=THIS_MODULE,
.open=Test_open,
.write=Test_write,
}
/* 初始化函式 */
int __init Test_dev_init(void)
{
int ret;
devno =MKDEV(major,minor);
ret=register_chrdev(major,module_name,&Test_fops); //在驅動鏈表中注冊驅動為字符設備
Test_class=class_create(THIS_MODULE,"Test_class");//創建類
Test_dev =device_create(pin4_class,NULL,devno,NULL,module_name);//創建驅動檔案
return 0;
}
3.撰寫剩下內容
void __exit Test_exit(void)
{
device_destroy(Test_class,devno);
class_destroy(Test_class);
unregister_chrdev(major,module_name);
}
module_init(Test_dev_init);
module_exit(Test_exit);
MODULE_LICENSE("GPL v2");
二、驅動模塊的編譯
寫好驅動之后需要把驅動檔案放到內核檔案的 /drivers/char/ 目錄下,即字符設備檔案夾:
cp Test.c /home/.../drivers/char/
然后在內核檔案的 /drivers/char/ 修改 Makefile 檔案,添加:
obj-m += test.o //obj-m 即編譯成模塊,

保存退出之后,回到內核檔案根目錄,樹莓派4使用
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7l make -j4 modules
一次性編譯驅動模塊:

然后根據提示修改錯誤,我這里提示36行附近少了分號 “;” ,結構體沒加分號,修改后再次編譯:

編譯成功之后會在 /drivers/char/ 目錄下生成一個以 .ko 為后綴的驅動模塊檔案,使用 scp 指令把這個 xxx.ko 驅動模塊檔案發送給樹莓派:
scp drivers/char/xxx.ko pi@/*樹莓派的IP地址*/:/home/xxx//*樹莓派的某個路徑*/

三、驅動模塊的安裝
在樹莓派里使用:
sudo insmod xxx.ko
sudo chmod 666 /dev/xxx
安裝驅動之后,給驅動添加權限,這樣我們用戶才能去使用這個驅動;

如果安裝完后,使用 ls /dev/xxx 指令沒有相關的模塊的話,請檢查驅動程式中創建類函式中的第二個是否都是小寫字母:

注意:這個引數不能和其他模塊有沖突;否則也會安裝失敗,可以
四、測驗驅動
驅動的測驗非常簡單,我們只要寫一個簡單的程式去運行就可以:
測驗程式代碼:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
int cmd=1;
fd=open("/dev/test",O_RDWR);
if(fd<0){
perror("reson");
return -1;
}
fd=write(fd,&cmd,sizeof(int));
return 0;
}

因為驅動里面列印的資訊在內核環境,所以上層環境看不到任何資訊,可使用:
dmesg
指令來查看內核的列印資訊,驅動的安裝錯誤提示也可以用該指令來查看,驅動的名字最好用小寫字母來命名;
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/242938.html
標籤:其他
