韓賢忠 胡業(yè)火
摘 要: 本文以PC-6325A板卡Linux系統(tǒng)下的驅動開發(fā)為例介紹了PC-6325A板卡信息及工作原理,linux內(nèi)核編譯加載、常用函數(shù)方法實現(xiàn)等基本編程技術??蔀長inux系統(tǒng)下驅動程序開發(fā)人員提供一定參考。
關鍵詞: Linux;驅動程序
1 引言
由于Linux系統(tǒng)具有運行安全穩(wěn)定、功能強大、獲取方便等眾多優(yōu)點,逐漸被眾多開發(fā)人員所使用。但市場上Linux發(fā)行版本多,很多硬件設備缺少對應版本的驅動程序,需要自行開發(fā)。
本文以PC-6325A板卡開發(fā)為例,介紹了linux內(nèi)核編譯加載、常用函數(shù)方法實現(xiàn)等基本編程技術,在后續(xù)驅動程序開發(fā)中可以此為參考。
2 硬件設備分析
PC-6325A模入接口卡適用于具有ISA總線的PC系列微機,具有很好的兼容性,CPU從目前廣泛使用的64位處理器到早期的16為處理器均可使用,操作系統(tǒng)也適用于MS-DOS、Windows系列、Unix等多種操作系統(tǒng)以及專業(yè)數(shù)據(jù)采集分析系統(tǒng)labVIEW等軟件環(huán)境。以下對PC-6325A板卡進行詳細介紹。
2.1 工作原理
PC-6325A板卡主要由模擬多路開關電路、放大器電路、模數(shù)轉換電路、接口控制邏輯電路、光電隔離電路及DC/DC電源電路組成。
2.1.1 模擬多路開關電路
模擬多路開關由4片8選1模擬開關芯片等組成,通過KJ1和KJ2跨接插座可以選擇32路單端或16路雙端輸入方式,并將選中的信號送入差分放大器處理。
2.1.2 模數(shù)轉換電路
PC-6325A卡選用新一代A/D器件ADS7808作為模數(shù)轉換器件。ADS7808內(nèi)部自帶采保和精密基準電源。A/D轉換可以由程序啟動,也可由外部觸發(fā)信號啟動。A/D轉換結束標志可以由程序查詢檢出,也可通過中斷方式通知CPU處理。
2.1.3 接口控制邏輯電路及光隔電路
接口控制邏輯電路用來產(chǎn)生與各種操作有關的控制信號。光隔電路采用6N137高速光耦對系統(tǒng)總線與模擬信號之間進行光電隔離,以避免相互間的干擾。
2.1.4 DC/DC電源電路
DC/DC電源電路有電源模塊及相關的濾波元件組成。該電源模塊的輸入電壓為+5V,輸出電壓為與原邊隔離的±15V和+5V,原付邊之間隔離電壓可達1500V。
3 驅動程序設計
3.1 編譯內(nèi)核
設備驅動屬于linux內(nèi)核的部分在編寫Linux設備驅動前需要對Linux操作系統(tǒng)內(nèi)核進行編譯。本次PC-6325板卡驅動開發(fā)調試采用2.6.23版本內(nèi)核。
編譯步驟如下:
1.下載并解壓Linux內(nèi)核一般內(nèi)核源碼放在/usr/src目錄下。
2.清除從前編譯內(nèi)核時殘留的.o文件和不必要的關聯(lián):
cd /usr/src/linux
make mrproper
確保源代碼目錄下沒有不正確的.o文件和文件依賴關系,執(zhí)行該命令后,內(nèi)核選項會回到默認的狀態(tài)下。如果為下載的內(nèi)核源碼,且為第一次編譯可跳過該步驟。
3.配置內(nèi)核,修改相關參數(shù):
圖形界面下,make xconfig;
字符界面下, make menuconfig;
在內(nèi)核配置菜單中正確設置內(nèi)核選項保存退出。
4.正確設置關聯(lián)文件:
make dep
根據(jù)上一步所選擇的選項,建立文件的依賴關系。
5.編譯內(nèi)核:
對于大內(nèi)核,make bzlmage
對于小內(nèi)核,mkae zlmage
6.編譯模塊:
make modules
編譯可加載模塊(即內(nèi)核選項中選擇為M的選項)。
7.安裝模塊:
make modules_install
將編譯好的modules拷貝到/lib/modules下(該步驟需要管理員權限)。
3.2 編寫驅動程序
內(nèi)核編譯完成后就可以進行驅動程序代碼編寫了,但在此之前需要了解一下Linux系統(tǒng)的一個基本概念,內(nèi)核空間和用戶空間。模塊運行在內(nèi)核空間用于擴展內(nèi)核功能,應用程序運行在用戶空間。內(nèi)核空間與用戶空間可理解為兩種運行模式,有著不同的優(yōu)先級及自己的內(nèi)存映射。相應的在代碼編寫中也會有內(nèi)核空間編程及用戶空間編程的區(qū)別。本文將以PC-6325A板卡驅動開發(fā)中的具體代碼為例對驅動程序中內(nèi)核空間及用戶空間較為常用的函數(shù)方法進行詳細介紹。
3.2.1 初始化和關閉
1.初始化代碼如下:
static int __init pc6325_init (void){
register_chrdev(MAJOR_NUM, "pc6325", &pc6325;_fops);
gPc6325Dev = kmalloc(sizeof(struct pc6325_dev), GFP_KERNEL);
init_MUTEX(&gPc6325Dev-;>sem); /*初始化信號量*/
return 0;
}
module_init(pc6325_init);
初始化函數(shù)聲明為static,該函數(shù)僅在初始化期間使用。模塊裝載之后,模塊裝載器將初始化函數(shù)丟掉,并釋放函數(shù)所占用的內(nèi)存。
module_init用于說明內(nèi)核初始化函數(shù)的位置。如果沒有這個定義,初始化函數(shù)將無法被調用。
2.清除函數(shù)代碼如下:
static void __exit pc6325_exit (void){
unregister_chrdev(MAJOR_NUM, "pc6325");
}
module_exit(pc6325_exit);
清除函數(shù)在模塊移除前注銷接口并返回系統(tǒng)中的所有資源。清除函數(shù)無返回值,聲明為void清除函數(shù)用于模塊卸載,在模塊卸載或者系統(tǒng)關閉時調用。
3.2.2 數(shù)據(jù)讀取與發(fā)送
1.讀取數(shù)據(jù)代碼如下
static ssize_t pc6325_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct pc6325_dev *dev = filp->private_data; /*獲得設備結構體指針*/
if (down_interruptible(&dev-;>sem)) /* 獲得信號量 */
{
return - ERESTARTSYS;
}
dev->mem[0]=inb(dev->address+p);
copy_to_user(buf, &(dev->mem[0]), count);
up(&dev-;>sem);
return ret;
}
該函數(shù)用來從設備讀取數(shù)據(jù),函數(shù)指針被賦予NULL值時,將導致reed系統(tǒng)調用出錯并返回-EINVAL(非法參數(shù))。函數(shù)返回非負值表示成功讀取的字節(jié)數(shù)。對于該方法,參數(shù)filp是文件指針,參數(shù)count是請求傳輸?shù)臄?shù)據(jù)長度。參數(shù)buff是用戶空間的指針,指向用戶空間的緩沖區(qū),保存要寫入的數(shù)據(jù),或者是一個存放新讀入數(shù)據(jù)的空緩沖區(qū)。最后的offp是一個指向長偏移量類型的對象指針,用于指明用戶在文件中進行存取操作的位置。
2.發(fā)送數(shù)據(jù)函數(shù)如下:
static ssize_t pc6325_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
unsigned char vData=0;
struct pc6325_dev *dev = filp->private_data; /* 獲得設備結構體指針 */
if( count<3 ){
return;
}
if (down_interruptible(&dev-;>sem)) /* 獲得信號量 */
{
return - ERESTARTSYS;
}
copy_from_user(dev->mem+p,buf,count);
if( count==3 && dev->mem[0]==0xED ){
dev->address=dev->mem[1]*0x100+dev->mem[2];
pc6325_set_base_address(dev->address);
}else if( count==3 && dev->mem[0]==0xCD ){
dev->offset=dev->mem[1];
vData=dev->mem[2];
outb(vData,dev->address+dev->offset);
}
up(&dev-;>sem); /* 釋放信號量 */
return ret;
}
向設備發(fā)送數(shù)據(jù)。如果返回值非負,則表示成功寫入的字節(jié)數(shù)。其用法及參數(shù)與read方法一致,在此不再贅述。
3.2.3 open和release方法
1.open方法:
int pc6325_open(struct inode *inode, struct file *filp)
{
filp->private_data = gPc6325Dev;
return 0;
}
Open方法提供給驅動程序初始化的能力,從而為以后的操作完成初始化的準備。在多數(shù)驅動程序中,open完成如下工作:
a)檢查設備特定的錯誤;
b)如果設備是首次打開,則對其進行初始化;
c)必要時,更新f_op指針;
d)非配并填寫置于filp->private_data里的數(shù)據(jù)結構[1]。
2.release方法:
int pc6325_release(struct inode *inode, struct file *filp)
{
struct pc6325_dev *dev = filp->private_data;
pc6325_release_base_address(dev->address);
//printk(KERN_ALERT "address release");
return 0;
}
release方法的作用于open正好相反。該方法通常完成以下任務。
a)釋放有open分配的、保存在filp->private_data中的所有內(nèi)容;
b)在最后一次關閉操作時關閉設備。
3.2.4 file_operations結構
代碼如下:
struct file_operations pc6325_fops={
read: pc6325_read,
write: pc6325_write,
open: pc6325_open,
release: pc6325_release,
owner: THIS_MODULE,
};
file_operations結構用于建立驅動程序與設備編號之間的連接。主要用于實現(xiàn)系統(tǒng)調用。結構中的每一個字段都指向驅動程序中特定操作的函數(shù),對于不支持的操作,對應的字段可設置為NULL值。
3.2.5 模塊加載與卸載
1. 模塊加載
# Create the device nodes (up to 10 by default)
echo -n "Creating device nodes........... "
rm -f ${path}/${name}*
mknod ${path}/${name} c $major 226
#mknod ${path}/${name} c 226 0
# Create additional nodes for non-service driver
if [ "${bServiceDriver}" == "0" ]; then
mknod ${path}/${name}-0 c $major 000
mknod ${path}/${name}-1 c $major 001
mknod ${path}/${name}-2 c $major 002
mknod ${path}/${name}-3 c $major 003
mknod ${path}/${name}-4 c $major 004
mknod ${path}/${name}-5 c $major 005
mknod ${path}/${name}-6 c $major 006
mknod ${path}/${name}-7 c $major 007
mknod ${path}/${name}-8 c $major 008
mknod ${path}/${name}-9 c $major 009
fi
chmod 777 $path
上述代碼中rm -f ${path}/${name}*的作用為卸載之前的模塊以釋放空間在模塊卸載中也會用到,之后的代碼為進行模塊加載。其中mknod命令用于創(chuàng)建設備文件。最后的chmod 777 $path表示賦予讀、寫以及運行的權限。
2.模塊卸載
echo -n "Clear existing device nodes..... "
rm -f $path/${name}*
echo "Ok (${path}/${name})"
卸載模塊代碼較為簡單使用rm -f $path/${name}*指令卸載即可。
4.結論
Linux系統(tǒng)對于開發(fā)人員而言有著眾多的優(yōu)點,但市場上Linux發(fā)行版本多,很多硬件設備缺少對應版本的驅動程序,需要自行開發(fā)。本文以PC-6325A板卡驅動開發(fā)為例介紹了PC-6325A板卡信息及工作原理,linux內(nèi)核編譯加載、常用函數(shù)方法實現(xiàn)等基本編程技術??蔀長inux系統(tǒng)驅動開發(fā)人員提供參考。
參考文獻
[1]LINUX設備驅動程序第三版,中國電力出版社,2006年1月,魏永明,耿岳,鐘書毅 譯.
[2]PC-6325A板卡用戶手冊.