轉載:https://www.cnblogs.com/han-bing/p/8807602.html
編寫一個最簡單的嵌入式Linux下的驅動程序大致分為以下幾步:
1.編寫驅動函數(shù)drive_xxx_open旷祸,drive_xxx_write搏予,drive_xxx_read等,這個需要根據(jù)實際情況盡心裁剪和添加。
一般情況下压昼,進行設備驅動程序的設計只是比較注重下面的幾個函數(shù):
.llseek =? ***_llseek, //文件定位
.read =? ***_read, ? //文件讀取
.write =? ***_write, //文件寫入
.ioctl =? ***_ioctl, ? ? //文件控制
.open =? ***_open, ? ? ? ?//文件打開
.release = ***_release, //文件釋放
本次驅動只做框架坑雅,用來打印一些信息即可缸沃,所以只填充open宜咒,write,read三個函數(shù)赘娄。
2.函數(shù)定義完成以后仆潮,對其進行封裝,也就是填入相應的file_operations結構體遣臼。
static struct file_operations first_drv_fops = {
? ? .owner? =? THIS_MODULE,? ? //這個成語并不是操作函數(shù)性置,它是一個指向擁有這個結構的模塊的指針,作用是當被使用時阻止模塊被卸載揍堰,可以簡單的定位為THIS_MODULE鹏浅。
.open = first_drv_open, //模塊打開
.write = first_drv_write, //模塊寫
.read? = first_drv_read,? ? ? //模塊度? };
3.驅動模塊加載函數(shù),通過函數(shù) register_chrdev(major, name, &fops) 來
注冊字符設備,定義一個first_drv_init入口 函數(shù)來調用這個它屏歹。
※register_chrdev函數(shù)會把設備添加到Linux系統(tǒng)module結構體的鏈表中隐砸。
1:參數(shù)分析
?* @major: major device number or 0 for dynamic allocation
?主設備號,當用戶設置為0時蝙眶,內核會動態(tài)分配一個設備號季希。
?* @name: name of this range of devices
設備名稱
?* @fops: file operations associated with this devices
文件系統(tǒng)的接口指針
4.通過module_init()來把入口函數(shù)裝入初始化啟動列表中,使內核知道有這個函數(shù)存在。
5.寫驅動的first_drv_exit出口函數(shù)械馆,調用這個unregister_chrdev()函數(shù)卸載模塊,然后通過module_exit()來修飾出口函數(shù)胖眷。
驅動代碼如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/*1寫出驅動程序first_drv_open first_drv_write */
/*? inode結構表示具體的文件,file結構體用來追蹤文件在運行時的狀態(tài)信息霹崎。*/
static int first_drv_open(struct inode *inode, struct file? *file)
{
? ? printk(“first_drv_open\n”);? ? ? //打印珊搀,在內核中打印只能用printk()
? ? return 0;
}
/*參數(shù)filp為目標文件結構體指針,buffer為要寫入文件的信息緩沖區(qū)尾菇,count為要寫入信息的長度境析,ppos為當前的偏移位置,這個值通常是用來判斷寫文件是否越界*/
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
? ? printk(“first_drv_write\n”);? ? ? //打印派诬,在內核中打印只能用printk()
? ? return 0;
}
static ssize_t first_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
? ? printk(“first_drv_read\n”);? ? ? //打印劳淆,在內核中打印只能用printk()
? ? return 0;
}
/*2定義file_operations結構體來封裝驅動函數(shù)first_drv_open first_drv_write */
static struct file_operations first_drv_fops = {
? ? .owner? =? THIS_MODULE,? ? //被使用時阻止模塊被卸載
? ? .open? ? =? first_drv_open,? ? ?
? ? .write? =? first_drv_write,?
? ? .read? ? =? first_drv_read,?
? };
/*4寫first_drv_init入口函數(shù)來調用這個register_chrdev()注冊函數(shù)*/
int first_drv_init(void)
{
? ? /*3 register_chrdev注冊字符設備,并設置major=111*/
? ? /*如果設置major為0,表示由內核動態(tài)分配主設備號默赂,函數(shù)的返回值是主設備號*/
? ? register_chrdev (111, “first_drv”, &first_drv_fops); //111:主設備號,”first_drv”:設備名
/*
register_chrdev作用:在VFS虛擬文件系統(tǒng)中找到字符設備沛鸵,然后通過主設備號找到內核數(shù)組里對應的位置,最后將設備名字和fops結構體填進去
*/
? return 0;
}
/*5 module_init修飾入口函數(shù)*/
module_init(first_drv_init);
/*6 寫first_drv_exit出口函數(shù)*/
void first_drv_exit(void)
{
unregister_chrdev (111, “first_drv”);? //卸載驅動,只需要主設備號和設備名就行
}
/*7 module_exit修飾出口函數(shù)*/
module_exit(first_drv_exit);
/*8許可證聲明, 描述內核模塊的許可權限,如果不聲明LICENSE,模塊被加載時曲掰,將收到內核被污染 (kernel tainted)的警告疾捍。*/
MODULE_LICENSE( "GPL v2" );