午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

Linux設(shè)備控制接口(ioctl 函數(shù))

 心不留意外塵 2016-07-26

http://www.cnblogs.com/noaming1900/archive/2010/10/20/1856581.html

2010

序言
設(shè)備驅(qū)動程序的一個基本功能就是管理和控制設(shè)備,同時為用戶應(yīng)用程序提供管理和控制設(shè)備的接口。我們前面的“Hello World”驅(qū)動程序已經(jīng)可以提供讀寫功能了,在這里我們將擴展我們的驅(qū)動以支持設(shè)備控制接口,在Linux中這個接口是通過ioctl函數(shù)來實現(xiàn)的。

設(shè)備控制接口(ioctl 函數(shù))
回想一下我們在字符設(shè)備驅(qū)動中介紹的struct file_operations 結(jié)構(gòu),這里我們將介紹一個新的方法:

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);


這是驅(qū)動程序設(shè)備控制接口函數(shù)(ioctl函數(shù))的內(nèi)核原型定義,struct inode *struct file* 描述了操作的文件,unsigned int 描述了ioctl命令號,這是一個重要的參數(shù),我們稍后會對它做詳細介紹。最后一個參數(shù)是unsigned long數(shù)據(jù)類型,描述了ioctl命令可能帶有的參數(shù),它可能是一個整數(shù)或指針數(shù)據(jù)。
  • ioctl命令號
ioctl命令號是這個函數(shù)中最重要的參數(shù),它描述的ioctl要處理的命令。Linux中使用一個32位的數(shù)據(jù)來編碼ioctl命令,它包含四個部分:dir:type:nr:size。
  • dir
代表數(shù)據(jù)傳輸?shù)姆较?,?位,可以是_IOC_NONE(無數(shù)據(jù)傳輸,0U),_IOC_WRITE(向設(shè)備寫數(shù)據(jù),1U)或_IOC_READ(從設(shè)備讀數(shù)據(jù),2U)或他們的邏輯或組合,當然只有_IOC_WRITE和_IOC_READ的邏輯或才有意義。
  • type
描述了ioctl命令的類型,8位。每種設(shè)備或系統(tǒng)都可以指定自己的一個類型號,ioctl用這個類型來表示ioctl命令所屬的設(shè)備或驅(qū)動。一般用ASCII碼字符來表示,如 'a'。
  • nr
ioctl命令序號,一般8位。對于一個指定的設(shè)備驅(qū)動,可以對它的ioctl命令做一個順序編碼,一般從零開始,這個編碼就是ioctl命令的序號。
  • size
ioctl命令的參數(shù)大小,一般14位。ioctl命令號的這個數(shù)據(jù)成員不是強制使用的,你可以不使用它,但是我們建議你指定這個數(shù)據(jù)成員,通過它我們可以檢查用戶空間數(shù)據(jù)的大小以避免錯誤的數(shù)據(jù)操作,也可以實現(xiàn)兼容舊版本的ioctl命令。
我們可以自己來直接指定一個ioctl命令號,它可能僅僅是一個整數(shù)集,但Linux中的ioctl命令號都是有特定含義的,因此通常我們不推薦這么做。其實Linux內(nèi)核已經(jīng)提供了相應(yīng)的宏來自動生成ioctl命令號:

_IO(type,nr)
_IOR(type,nr,size)
_IOW(type,nr,size)
_IOWR(type,nr,size)


宏_IO用于無數(shù)據(jù)傳輸,宏_IOR用于從設(shè)備讀數(shù)據(jù),宏 _IOW用于向設(shè)備寫數(shù)據(jù),宏_IOWR用于同時有讀寫數(shù)據(jù)的IOCTL命令。相對的,Linux內(nèi)核也提供了相應(yīng)的宏來從ioctl命令號種解碼相應(yīng)的域值:

_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)


這些宏都定義在<asm/ioctl.h>頭文件中(一般在<asm-generic.h>頭文件中)。一般在使用中,先指定各個IOCTL命令的順序編號(一般從0開始),然后根據(jù)使用的環(huán)境用這些宏來自動生成IOCTL命令號,在后面的例子中你可以了解實際的使用場景。
  • ioctl返回值
ioctl函數(shù)的返回值是一個整數(shù)類型的值,如果命令執(zhí)行成功,ioctl返回零,如果出現(xiàn)錯誤,ioctl函數(shù)應(yīng)該返回一個負值。這個負值會作為errno值反饋給調(diào)用此ioctl的用戶空間程序。關(guān)于返回值的具體含義,請參考<linux/errno.h>和<asm/errno.h>頭文件。
  • ioctl參數(shù)
這里有必要說明一下ioctl命令的參數(shù),因為它很容易犯錯誤。如果ioctl命令參數(shù)僅僅是一個整數(shù),那么事情很簡單了,我們可以在ioctl函數(shù)中直接使用它。但如果它是一個指針數(shù)據(jù),那么使用上就要小心了。首先要說明這個參數(shù)是有用戶空間的程序傳遞過來的,因此這個指針指向的地址是用戶空間地址,在Linux中,用戶空間地址是一個虛擬地址,在內(nèi)核空間是無法直接使用它的。為了解決在內(nèi)核空間使用用戶空間地址的數(shù)據(jù),Linux內(nèi)核提供了以下函數(shù),它們用于在內(nèi)核空間訪問用戶空間的數(shù)據(jù),定義在<asm/uaccess.h>頭文件中:

unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
const void __user *from, unsigned long n);


copy_from_user和copy_to_user一般用于復(fù)雜的或大數(shù)據(jù)交換,對于簡單的數(shù)據(jù)類型,如int或char,內(nèi)核提供了簡單的宏來實現(xiàn)這個功能:

#define get_user(x,ptr)
#define put_user(x,ptr)


其中,x是內(nèi)核空間的簡單數(shù)據(jù)類型地址,ptr是用戶空間地址指針。
我們需要牢記:在內(nèi)核中是無法直接訪問用戶空間地址數(shù)據(jù)的。因此凡是從用戶空間傳遞過來的指針數(shù)據(jù),務(wù)必使用內(nèi)核提供的函數(shù)來訪問它們。
這里有必要再一次強調(diào)的是,在內(nèi)核模塊或驅(qū)動程序的編寫中,我們強烈建議你使用內(nèi)核提供的接口來生成并操作ioctl命令號,這樣可以對命令號賦予特定的含義,使我們的程序更加的健壯;另一方面也可以提高程序的可移植性。

舉例
好了,是時候舉個例子了。我們將擴展我們的helloworld驅(qū)動添加ioctl函數(shù)。

首先,我們添加一個頭文件來定義ioctl接口需要用到的數(shù)據(jù)(hello.h):

#ifndef _HELLO_H
#define _HELLO_H
#include <asm/ioctl.h>
#define MAXBUF 20
typedef struct _buf_data{
int size;
char data [MAXBUF];
}buf_data;

#define HELLO_IOCTL_NR_BASE            0
#define HELLO_IOCTL_NR_SET_DATA     (HELLO_IOCTL_NR_BASE + 1)
#define HELLO_IOCTL_NR_MAX             (HELLO_IOCTL_NR_GET_BUFF + 1)

#define HELLO_IOCTL_SET_DATA           _IOR('h', HELLO_IOCTL_NR_SET_DATA, buf_data*)

#endif


然后為我們的驅(qū)動程序添加ioctl接口hello_ioctl,并實現(xiàn)這個函數(shù):

static int hello_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int cmd_nr;
int err;
buf_data buff;

err = 0;
cmd_nr = _IOC_NR (cmd);
switch (cmd_nr){
case HELLO_IOCTL_NR_SET_DATA:
if (copy_from_user(&buff, (unsigned char *)arg, sizeof(buf_data)))
{
err = -ENOMEM;
goto error;
}
memset(hello_buf, 0, sizeof(hello_buf));
memcpy(hello_buf, buff.data, buff.size);
break;
default:
printk("hello_ioctl: Unknown ioctl command (%d)\n", cmd);
break;
}

error:
return err;
}

static struct file_operations hello_fops = {
.read = hello_read,
.write = hello_write,
.open = hello_open,
.ioctl = hello_ioctl,
.release = hello_release,
};



后記
到這里我們已經(jīng)向您展示了Linux內(nèi)核驅(qū)動程序的設(shè)備控制接口(ioctl接口),詳細的介紹了它的使用,并給出了一個實際的例子,盡管它很簡單,但已經(jīng)足夠了。到這里你可以寫出一個標準的Linux驅(qū)動程序了。不過這里還有個問題,那就是我們不得不從/proc/devices文件里讀取設(shè)備號然后手動創(chuàng)建設(shè)備節(jié)點。我們是否可以讓系統(tǒng)自動的創(chuàng)建這個設(shè)備節(jié)點文件呢?當然可以。不過在那之前,我們必須深入了解Linux的設(shè)備驅(qū)動模型。后面的章節(jié)我們就詳細的介紹Linux的設(shè)備驅(qū)動模型及Hotplug機制。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多