489
技術社區[雲棲]
Linux字符設備驅動
1、預備知識:
應用程序、庫、內核、驅動程序的關係
應用程序調用應用程序函數庫完成功能
應用程序以文件形式訪問各種資源
應用程序函數庫
部分函數直接完成功能
部分函數通過係統調用由內核完成
內核處理係統調用,調用設備驅動程序
設備驅動直接與硬件通信
設備類型
字符設備
對字符設備發出讀/寫請求時,實際的硬件I/O操作一般緊接著發生
塊設備
塊設備與之相反,它利用係統內存作為緩衝區
網絡設備
網絡設備是一類特殊的設備,它不像字符設備或塊設備那樣通過對應的設備文件節點訪問,也不能直接通過read或write進行數據訪問請求
主設備號與從設備號
在設備管理中,除了設備類型外,內核還需要一對被稱為主從設備號的參數,才能唯一標識一個設備
主設備號相同的設備使用相同的驅動程序
從設備號用於區分具體設備的實例
cat /proc/devices可以查看係統中所有設備對應的主設備號
設備文件
設備類型、主從設備號是內核與設備驅動程序通信時使用的
應用程序使用設備文件節點訪問對應設備
每個主從設備號確定的設備都對應一個文件節點
每個設備文件都有其文件屬性(c或者b)
每個設備文件都有2個設備號(主,從)
設備文件的主設備號必須與設備驅動程序在登記時申請的主設備號一致
係統調用是內核與應用程序之間的接口
設備驅動程序是內核與硬件之間的接口
驅動程序與應用程序的區別
應用程序以main開始
驅動程序沒有main,它以一個模塊初始化函數作為入口
應用程序從頭到尾執行一個任務
驅動程序完成初始化之後不再運行,等待係統調用
應用程序可以使用GLIBC等標準C函數庫
驅動程序不能使用標準C庫
用戶態與內核態
驅動程序是內核的一部分,工作在內核態。 應用程序工作在用戶態
數據空間訪問問題
無法通過指針直接將二者的數據地址進行傳遞
係統提供一係列函數幫助完成數據空間轉換 copy_from_user copy_to_user
Linux驅動程序功能
對設備初始化和釋放
把數據從內核傳送到硬件和從硬件讀取數據
讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據
檢測和處理設備出現的錯誤

2、Linux字符設備驅動框架
file_operations結構體
規定了驅動程序向應用程序提供的操作接口
實現函數:
open 當應用程序打開設備時對設備進行初始化
release 關閉設備時處理關閉操作
read 從硬件讀取數據並交給應用程序
write 從應用程序接收數據送到硬件
ioctl 為應用程序提供對硬件行為的控製
3、Linux字符設備驅動框架
Linux在加載內核模塊時會調用初始化函數
static int __int XXXX_init(void)
使用register_chrdev向內核注冊驅動程序
驅動退出:
Linux在卸載內核模塊時會調用退出函數
static void __exit XXXX_exit(void)
使用unregister_chrdev從內核中卸載驅動程序
內核需要知道模塊的初始化函數和退出函數,才能將模塊放入自己的管理隊列中
module_init(XXXX_init)
向內核聲明當前模塊的初始化函數
module_exit(XXXX_exit)
向內核聲明當前模塊的退出函數
4、中斷處理
釋放中斷: free_irq()
禁止單個中斷: disable_irq()
允許單個中斷: enable_irq()
禁止所有中斷: local_irq_disable()
允許所有中斷: local_irq_enable()
5、同步機製
內核需要提供並發控製機製,對公共資源的訪問進行同步控製,確保共享資源的安全訪問。
Linux同步機製:自旋鎖(spinlock),信號量(semaphore),讀寫鎖(rwlock),
順序鎖(seqlock),RCU(Read-Copy Update)
#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 <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/gpio.h>
#define DEVICE_NAME "mydriver"
static int MYDRIVER_Major = 0;//為0,係統自動分配設備號。
static int mydriver_open(struct inode *inode, struct file *file)
{
printk("My Driver Open Called!\n");
return 0;
}
static int mydriver_release(struct inode *inode, struct file *file)
{
printk("My Driver Release Called!\n");
return 0;
}
static int mydriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
printk("My Driver Read Called!\n");
return 0;
}
static int mydriver_write(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
printk("My Driver Write Called!\n");
return 0;
}
static int mydriver_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
}
static struct file_operations mydriver_fops =
{
.owner = THIS_MODULE,
.open = mydriver_open,
.release = mydriver_release,
.read = mydriver_read,
.write = mydriver_write,
.ioctl = mydriver_ioctl,
};
static struct class *mydriver_class;
static int __init mydriver_init(void)
{
printk("MY DRIVER MODULE INIT\n");
MYDRIVER_Major = register_chrdev(0, DEVICE_NAME, &mydriver_fops);
if (MYDRIVER_Major < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return MYDRIVER_Major;
}
printk("register My Driver OK! Major = %d\n", MYDRIVER_Major);
//注冊一個類,使mdev可以在"/dev/"目錄下麵建立設備節點
mydriver_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(mydriver_class))
{
printk("Err: failed in My Driver class. \n");
return -1;
}
//創建一個設備節點,節點名為DEVICE_NAME
device_create(mydriver_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return 0;
}
static void __exit mydriver_exit(void)
{
printk("MY DRIVER MODULE EXIT\n");
unregister_chrdev(MYDRIVER_Major, DEVICE_NAME);
device_destroy(mydriver_class, MKDEV(MYDRIVER_Major, 0));
class_destroy(mydriver_class);
}
module_init(mydriver_init);
module_exit(mydriver_exit);
MODULE_AUTHOR("www.txmcu.com");
MODULE_DESCRIPTION("My Driver");
MODULE_LICENSE("GPL");
測試程序模板:#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int fd;
fd = open("/dev/XXXX", 0);
if (fd < 0)
{
perror("open device");
exit(1);
}
/*your code*/
close(fd);
return 0;
}
最後更新:2017-04-03 14:53:38
上一篇:
VC++獲取網卡MAC、硬盤序列號、CPU ID、BIOS編號
下一篇:
win7方麵API學習
難道調用ThreadPool.QueueUserWorkItem()的時候,真是必須調用Thread.Sleep(N)嗎?
提高顯示布局文件的性能 3 - 按需載入視圖(ViewStub的使用方法)
Java IO--字節-字符流轉換OutputStreamWriter/InputStreamReader
《軟件工藝師:專業、務實、自豪》一2.6.2 局部轉型的積極意義
virtual 與 abstract 區別
工作中要拿出自己的態度
mysql 清空二進製日誌
PHP培訓機構排名
wot創新技術峰會3個子專題介紹
WCF 技術剖析之三十三:你是否了解WCF事務框架體係內部的工作機製?[下篇]