1. 程式人生 > >ioctl()函式學習 【綜合貼】

ioctl()函式學習 【綜合貼】

目錄:

    6、linux系統檔案

說明:本文為上述文章的綜合,但有些地方根據自己的理解做了編輯更改。雖有些許原創,但可忽略不計。本文只為學習更方便,不保留版權。

標頭檔案: #include<sys/ioctl.h>

功  能:  控制I/O裝置 ,提供了一種獲得裝置資訊和向裝置傳送控制引數的手段。用於向裝置發控制和配置命令,有些命令需要控制引數,這些資料是不能用read / write 讀寫的,稱為Out-of-band資料。也就是說,read / write 讀寫的資料是in-band資料,是I/O操作的主體,而ioctl命令傳送的是控制資訊,其中的資料是輔助的資料。

用  法:  int ioctl(int handle, unsigned long cmd,[int *argdx, int argcx]);

返回值: 成功為0,出錯為-1

     實際上在我的linux系統(redhat9,linux2.4核心和升級到的linux2.6)中,有三處ioctl.h標頭檔案:

     /usr/include/asm/ioctl.h:這個檔案定義了關於ioctl要用到的許多至關重要的巨集定義,在後邊要講到。

     /usr/include/linux/ioctl.h:包含的上一個檔案

     /usr/include/sys/ioctl.h:除了包含了好幾個檔案外,就是一個ioctl的extern宣告,形式如下:externint ioctl(int __fd, unsigned long int __request, ...)。這個檔案就是提供給了使用者空間一個ioctl控制函式。由於這些點而凸現於 Unix 系統呼叫列表, 這些點常常表示函式有數目不定的引數. 在實際系統中, 但是, 一個系統呼叫不能真正有變數目的引數. 系統呼叫必須有一個很好定義的原型, 因為使用者程式可存取它們只能通過硬體的"門". 因此, 原型中的點不表示一個變數目的引數, 而是一個單個可選的引數, 傳統上標識為 char *argp. 這些點在那裡只是為了阻止在編譯時的型別檢查. 第 3 個引數的實際特點依賴所發出的特定的控制命令( 第 2 個引數 ). 一些命令不用引數, 一些用一個整數值, 以及一些使用指向其他資料的指標. 使用一個指標就可以傳遞任意資料到 ioctl 呼叫的方法; 裝置接著可與使用者空間交換任何數量的資料.

第二篇:核心空間的ioctl----用於驅動程式

函式形式:

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

這是驅動程式裝置控制介面函式(ioctl函式)的核心原型定義,struct inode *struct file*描述了操作的檔案,unsigned int 描述了ioctl命令號,這是一個重要的引數,我們稍後會對它做詳細介紹。最後一個引數是unsigned long資料型別,描述了ioctl命令可能帶有的引數,它可能是一個整數或指標資料。

  • ioctl命令號

ioctl

命令號是這個函式中最重要的引數,它描述的ioctl要處理的命令。Linux中使用一個32位的資料來編碼ioctl命令,它包含四個部分:dir:type:nr:size。

·        dir

代表資料傳輸的方向,佔2位,可以是_IOC_NONE(無資料傳輸,0U),_IOC_WRITE(向裝置寫資料,1U)或_IOC_READ(從裝置讀資料,2U)或他們的邏輯或組合,當然只有_IOC_WRITE和_IOC_READ的邏輯或才有意義。

·        type

描述了ioctl命令的型別,8位。每種裝置或系統都可以指定自己的一個型別號,ioctl用這個型別來表示ioctl命令所屬的裝置或驅動。一般用ASCII碼字元來表示,如 'a'。

·        nr

ioctl命令序號,一般8位。對於一個指定的裝置驅動,可以對它的ioctl命令做一個順序編碼,一般從零開始,這個編碼就是ioctl命令的序號。

·        size

ioctl命令的引數大小,一般14位。ioctl命令號的這個資料成員不是強制使用的,你可以不使用它,但是我們建議你指定這個資料成員,通過它我們可以檢查使用者空間資料的大小以避免錯誤的資料操作,也可以實現相容舊版本的ioctl命令。

我們可以自己來直接指定一個ioctl命令號,它可能僅僅是一個整數集,但Linux中的ioctl命令號都是有特定含義的,因此通常我們不推薦這麼做。其實Linux核心已經提供了相應的巨集來自動生成ioctl命令號:

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

巨集_IO用於無資料傳輸,巨集_IOR用於從裝置讀資料,巨集 _IOW用於向裝置寫資料,巨集_IOWR用於同時有讀寫資料的IOCTL命令。相對的,Linux核心也提供了相應的巨集來從ioctl命令號種解碼相應的域值:

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

這些巨集都定義在<asm/ioctl.h>標頭檔案中(一般在<asm-generic.h>標頭檔案中)。一般在使用中,先指定各個IOCTL命令的順序編號(一般從0開始),然後根據使用的環境用這些巨集來自動生成IOCTL命令號,在後面的例子中你可以瞭解實際的使用場景。

  • ioctl返回值

ioctl函式的返回值是一個整數型別的值,如果命令執行成功,ioctl返回零,如果出現錯誤,ioctl函式應該返回一個負值。這個負值會作為errno值反饋給呼叫此ioctl的使用者空間程式。關於返回值的具體含義,請參考<linux/errno.h>和<asm/errno.h>標頭檔案。

  • ioctl引數

這裡有必要說明一下ioctl命令的引數,因為它很容易犯錯誤。如果ioctl命令引數僅僅是一個整數,那麼事情很簡單了,我們可以在ioctl函式中直接使用它。但如果它是一個指標資料,那麼使用上就要小心了。首先要說明這個引數是有使用者空間的程式傳遞過來的,因此這個指標指向的地址是使用者空間地址,在Linux中,使用者空間地址是一個虛擬地址,在核心空間是無法直接使用它的。為了解決在核心空間使用使用者空間地址的資料,Linux核心提供了以下函式,它們用於在核心空間訪問使用者空間的資料,定義在<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一般用於複雜的或大資料交換,對於簡單的資料型別,如int或char,核心提供了簡單的巨集來實現這個功能:

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

其中,x是核心空間的簡單資料型別地址,ptr是使用者空間地址指標。
我們需要牢記:在核心中是無法直接訪問使用者空間地址資料的。因此凡是從使用者空間傳遞過來的指標資料,務必使用核心提供的函式來訪問它們。

這裡有必要再一次強調的是,在核心模組或驅動程式的編寫中,我們強烈建議你使用核心提供的介面來生成並操作ioctl命令號,這樣可以對命令號賦予特定的含義,使我們的程式更加的健壯;另一方面也可以提高程式的可移植性。舉例好了,是時候舉個例子了。我們將擴充套件我們的helloworld驅動新增ioctl函式。首先,我們新增一個頭檔案來定義ioctl介面需要用到的資料(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

然後為我們的驅動程式新增ioctl介面hello_ioctl,並實現這個函式:

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,
};

這個例子儘管很簡單,但已經足夠了。到這裡你可以寫出一個標準的Linux驅動程式了。不過這裡還有個問題,那就是我們不得不從/proc/devices檔案裡讀取裝置號然後手動建立裝置節點。我們是否可以讓系統自動的建立這個裝置節點檔案呢?當然可以。不過在那之前,我們必須深入瞭解Linux的裝置驅動模型。

第三篇:標頭檔案ioctl.h----關於ioctl的巨集定義

在編寫ioctl程式碼之前,需要選擇對應不同命令的編號。為了防止對錯誤的裝置使用正確的命令,命令號應該在系統範圍內唯一,這種錯誤匹配並不是不會發生,程式可能發現自己正在試圖對FIFOaudio等這類非序列裝置輸入流修改波特率,如果每一個ioctl命令都是唯一的,應用程式進行這種操作時就會得到一個EINVAL錯誤,而不是無意間成功地完成了意想不到的操作。

要按Linux核心的約定方法為驅動程式選擇ioctl編號,應該首先看看include/asm/ioctl.h這兩個檔案。標頭檔案定義了要使用的位欄位:型別(幻數)、序數、傳送方向以及引數大小等。ioctl-number.txt檔案中羅列了核心所使用的幻數,選擇自己的幻數要避免和核心衝突。以下是對include/asm/ioctl.h中定義的巨集的註釋:

#define_IOC_NRBITS          8                               //序數(number)欄位的字位寬度,8bits

#define_IOC_TYPEBITS      8                               //幻數(type)欄位的字位寬度,8bits

#define        _IOC_SIZEBITS       14                              //大小(size)欄位的字位寬度,14bits

#define_IOC_DIRBITS         2                               //方向(direction)欄位的字位寬度,2bits

#define_IOC_NRMASK        ((1 << _IOC_NRBITS)-1)    //序數字段的掩碼,0x000000FF

#define         _IOC_TYPEMASK   ((1 << _IOC_TYPEBITS)-1)    //幻數字段的掩碼,0x000000FF

#define         _IOC_SIZEMASK     ((1 << _IOC_SIZEBITS)-1)   //大小欄位的掩碼,0x00003FFF

#define_IOC_DIRMASK      ((1 << _IOC_DIRBITS)-1)    //方向欄位的掩碼,0x00000003

#define        _IOC_NRSHIFT       0                                                         //序數字段在整個欄位中的位移,0

#define        _IOC_TYPESHIFT   (_IOC_NRSHIFT+_IOC_NRBITS)         //幻數字段的位移,8

#define_IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)  //大小欄位的位移,16

#define        _IOC_DIRSHIFT      (_IOC_SIZESHIFT+_IOC_SIZEBITS)    //方向欄位的位移,30

/*

 * Direction bits.

 */

#define_IOC_NONE     0U     //沒有資料傳輸

#define _IOC_WRITE   1U     //向裝置寫入資料,驅動程式必須從使用者空間讀入資料

#define_IOC_READ     2U     //從裝置中讀取資料,驅動程式必須向用戶空間寫入資料

/*

*_IOC 巨集將dirtypenrsize四個引數組合成一個cmd引數,如下圖:

*

*/

#define _IOC(dir,type,nr,size) \

       (((dir)  << _IOC_DIRSHIFT) | \

        ((type) << _IOC_TYPESHIFT) | \

        ((nr)   << _IOC_NRSHIFT) | \

        ((size) << _IOC_SIZESHIFT))

/*

* used to create numbers 

*/

//構造無引數的命令編號

#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr),0) 

//構造從驅動程式中讀取資料的命令編號

#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size)) 

//用於向驅動程式寫入資料命令

#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))

//用於雙向傳輸

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* 

*used to decode ioctl numbers..

 */

//從命令引數中解析出資料方向,即寫進還是讀出

#define _IOC_DIR(nr)          (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

//從命令引數中解析出幻數type

#define _IOC_TYPE(nr)              (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

//從命令引數中解析出序數number

#define _IOC_NR(nr)           (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

//從命令引數中解析出使用者資料大小

#define _IOC_SIZE(nr)         (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN            (_IOC_WRITE << _IOC_DIRSHIFT)

#define IOC_OUT         (_IOC_READ << _IOC_DIRSHIFT)

#defineIOC_INOUT     ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)

#define IOCSIZE_MASK      (_IOC_SIZEMASK << _IOC_SIZESHIFT)

#define IOCSIZE_SHIFT      (_IOC_SIZESHIFT)

我的理解:

假如我定義了一個命令MY_CMD:

#define MY_CMD_MAGIC0xdf             //type欄位,由於欄位寬度為8 bits,所以不能大於0xff

#define MY_CMD  _IOW(MY_CMD_MAGIC,0,unsignedint)    

於是有命令MY_CMD各組成欄位(dirsizetypenr)分別為:01(=_IOC_WRITE),00 0000 0000 0100(=sizeof(unsignedint)),1101 1111(=MY_CMD_MAGIC),0000 0000(=0)。用十六進位制表示即0x4004df00。這個32 bits整數就是該命令的編號(LDD3原文是command number),也就是命令MY_CMD在系統中的身份證號碼了!

為什麼上面我用身份證號碼作比喻呢?眾所周知,一個國家裡所有公民的身份證號都是各不相同的。身份證編號有一定的規則:即把身份證號劃分成幾個欄位,各段位數可不等,每個欄位編碼都有它的實際意義,例如我們現在用的的身份證前N位(不記得具體是多少了)表示一個具體的省、市、等地區,不同地區的人該欄位肯定不同了;另外有個表示出生年月的欄位(據說以前整個號碼最後一位偶數表示男性,奇數表示女性,現在貌似沒這個規則了,此為題外話)。

類似地,我們要為系統裡所有的IOCTL命令編號。我們身份證用的是15(上一代是18位)位十進位制數編碼(最後一位可能是拉丁字母);我們用32位二進制數為IOCTL命令編碼,把它劃分成4個欄位,每段也有它的實際意義。要保證每個命令編號為系統唯一,主要靠命令的typenr欄位。我們稱type 欄位內容為magic number,即幻數,它表示命令的型別

看到上段紅色這句話,我想可能有細心的人會問:那麼命令到底有哪些型別呢?老實說我也不知道正確答案。大概因為大家都知道基本資料型別有整形、浮點型、字元型...人的性格型別有外向型、內向型...這些我們常見的型別都是可以用文字來列舉描述的,所以潛意識就覺得有型別就應該有文字可描述吧。回到正題,我想每個magic number,就像上面的巨集定義中:#defineMY_CMD_MAGIC 0xdfMY_CMD_MAGIC就是型別名了吧!不知道我的想法對不對?!反正大家知道一個magic number就對應唯一一種命令型別就是了。LDD原文中有一段:

type
The magic number. Just choose one number (after consulting ioctl-number.txt)
and use it throughout the driver. This field is eight bits wide(_IOC_TYPEBITS).

原文是說type的內容叫幻數(magic number),強調它是一個8位二進位制數(number)。因此不同type的命令就有不同的magic number,因此命令編碼自然就不同了。但如果兩個命令type相同,它們的magic number就相同,於是就不能僅靠type欄位區分了。於是nr欄位就起作用了:

number
The ordinal (sequential) number. It’s eight bits (_IOC_NRBITS) wide.

nr
number)即序號,一般地我們從0開始編號。由於nr欄位為8位二進位制數,所以nr的取值範圍為0~255。同一種type的命令每個nr值對應唯一一個命令。其他兩個欄位在這裡不作深究。值得一提的是LDD3裡這麼一段話:The header alsodefines macros that may be used in your driver to decode the num-bers:_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), and _IOC_SIZE(nr). We won’t gointo any moredetail about these macros because the header file is clear, and samplecode is shownlater in this section.根據ioctl.h檔案的定義,很明顯_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)這幾個巨集裡面的引數就是一個IOCTL命令,即一個32位的二進位制數,而檔案中引數居然用nr表示!當然用nr表示不是邏輯上的錯誤。但是別忘了檔案中還有_IOW(type,nr,size)這樣的定義IOCTL命令的巨集,這其中的引數nr IOCTL命令編號中的一個nr欄位,一個8位的二進位制數!我想很多新人都會對此產生莫大的疑惑!所以我認為把_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)這幾個解碼巨集的引數改用cmd表示更恰當!但是,我知道這不是LDD3作者的錯,因為核心標頭檔案裡面也是這麼表示的。我想核心開發者不可能沒意識到這個問題。因此,我猜測這其中肯定有個歷史原因:大概以前版本的命令不管type是否一樣,nr欄位的值都是唯一的,於是僅靠nr欄位就可以解碼出一個IOCTL命令的其他欄位吧?!但即使這樣 _IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)也沒必要保留這種寫法啊!到底誰可以告訴我真相?LDD3沒有對_IOC_DIR(nr), _IOC_TYPE(nr),_IOC_NR(nr),_IOC_SIZE(nr)裡面的nr作任何解釋,只是例項中有如下用法:if(_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;if (_IOC_NR(cmd)> SCULL_IOC_MAXNR) return -ENOTTY;可見那個nr引數完全就是我所說的32位的IOCTL命令編碼。靠,既然這樣好歹也對著個confusion作一下簡單的解釋啊!如果LDD3一書那不承認,也不否認的曖昧態度讓我真讓我哭笑不得的話,那麼國內某書(具體哪本我就不說了)簡直令我抓狂,我摘書中的兩段話如下:_IO(type,nr):定義一個沒有資料傳輸的命令編號。type為幻數,nr命令編號......_IOC_DIR(nr): 獲得命令編號的命令傳輸方向(direction)。這個巨集的引數就是命令編號。

由於檔案不長,且其中的英文比較簡單 這裡直接貼原檔案內容:

If youare adding new ioctl's to the kernel, you should use the _IO

macrosdefined in <linux/ioctl.h>:

    _IO   an ioctl with no parameters

    _IOW  an ioctl with write parameters (copy_from_user)

    _IOR  an ioctl with read parameters (copy_to_user)

    _IOWR an ioctl with both write and read parameters.

'Write'and 'read' are from the user's point of view, just like the

systemcalls 'write' and 'read'.  For example, aSET_FOO ioctl would

be _IOW,although the kernel would actually read data from user space;

a GET_FOOioctl would be _IOR, although the kernel would actually write

data touser space.

The firstargument to _IO, _IOW, _IOR, or _IOWR is an identifying letter

or numberfrom the table below.  Because of thelarge number of drivers,

many driversshare a partial letter with other drivers.

If youare writing a driver for a new device and need a letter, pick an

unusedblock with enough room for expansion: 32 to 256 ioctl commands.

You canregister the block by patching this file and submitting the

patch toLinus Torvalds.  Or you can e-mail me at<[email protected]> and

I'llregister one for you.

Thesecond argument to _IO, _IOW, _IOR, or _IOWR is a sequence number

todistinguish ioctls from each other.  Thethird argument to _IOW,

_IOR, or_IOWR is the type of the data going into the kernel or coming

out ofthe kernel (e.g.  'int' or 'struct foo').

Somedevices use their major number as the identifier; this is OK, as

long asit is unique.  Some devices are irregularand don't follow any

conventionat all.

Followingthis convention is good because:

(1)Keeping the ioctl's globally unique helps error checking:

    if a program calls an ioctl on the wrongdevice, it will get an

    error rather than some unexpectedbehaviour.

(2) The'strace' build procedure automatically finds ioctl numbers

    defined with _IO, _IOW, _IOR, or _IOWR.

(3)'strace' can decode numbers back into useful names when the

    numbers are unique.

(4)People looking for ioctls can grep for them more easily when

    this convention is used to define the ioctlnumbers.

(5) Whenfollowing the convention, the driver code can use generic

    code to copy the parameters between userand kernel space.

Thistable lists ioctls visible from user land for Linux/i386.  It contains

mostdrivers up to 2.3.14, but I know I am missing some.

Code    Seq#    IncludeFile        Comments

========================================================

0x00    00-1F   linux/fs.h      conflict!

0x00    00-1F   scsi/scsi_ioctl.h   conflict!

0x00    00-1F   linux/fb.h      conflict!

0x00    00-1F   linux/wavefront.h   conflict!

0x02    all linux/fd.h

0x03    all linux/hdreg.h

0x04    all linux/umsdos_fs.h

0x06    all linux/lp.h

0x09    all linux/md.h

0x12    all linux/fs.h

        linux/blkpg.h

0x20    all drivers/cdrom/cm206.h

0x22    all scsi/sg.h

'1' 00-1F   <linux/timepps.h>   PPS kit from Ulrich Windl

                    <ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>

'6' 00-10   <asm-i386/processor.h>  Intel IA32 microcode update driver

                    <mailto:[email protected]>

'8' all             SNP8023advanced NIC card

                    <mailto:[email protected]>

'A' 00-1F   linux/apm_bios.h

'B' C0-FF               advancedbbus

                    <mailto:[email protected]>

'C' all linux/soundcard.h

'D' all asm-s390/dasd.h

'F' all linux/fb.h

'I' all linux/isdn.h

'J' 00-1F   drivers/scsi/gdth_ioctl.h

'K' all linux/kd.h

'L' 00-1F   linux/loop.h

'L' E0-FF   linux/ppdd.h        encrypted disk device driver

                    <http://linux01.gwdg.de/~alatham/ppdd.html>

'M' all linux/soundcard.h   conflict!

'M' 00-1F   linux/isicom.h      conflict!

'N' 00-1F   drivers/usb/scanner.h

'P' all linux/soundcard.h

'Q' all linux/soundcard.h

'R' 00-1F   linux/random.h

'S' all linux/cdrom.h       conflict!

'S' 80-81   scsi/scsi_ioctl.h   conflict!

'S' 82-FF   scsi/scsi.h     conflict!

'T' all linux/soundcard.h   conflict!

'T' all asm-i386/ioctls.h   conflict!

'U' all linux/drivers/usb/usb.h

'V' all linux/vt.h

'W' 00-1F   linux/watchdog.h    conflict!

'W' 00-1F   linux/wanrouter.h   conflict!

'X' all linux/xfs_fs.h

'Y' all linux/cyclades.h

'a' all             ATMon linux

                    <http://lrcwww.epfl.ch/linux-atm/magic.html>

'b' 00-FF               bit3vme host bridge

                    <mailto:[email protected]>

'c' 00-7F   linux/comstats.h    conflict!

'c' 00-7F   linux/coda.h        conflict!

'd' 00-1F   linux/devfs_fs.h    conflict!

'd' 00-DF   linux/video_decoder.h   conflict!

'd' F0-FF   linux/digi1.h

'e' all linux/digi1.h       conflict!

'e' 00-1F   linux/video_encoder.h   conflict!

'e' 00-1F   net/irda/irtty.h    conflict!

'f' 00-1F   linux/ext2_fs.h

'h' 00-7F               Charonfilesystem

                    <mailto:[email protected]>

'i' 00-3F   linux/i2o.h

'j' 00-3F   linux/joystick.h

'k' all asm-sparc/kbio.h

        asm-sparc64/kbio.h

'l' 00-3F   linux/tcfs_fs.h     transparent cryptographic file system

                    <http://mikonos.dia.unisa.it/tcfs>

'l' 40-7F   linux/udf_fs_i.h    in development:

                    <http://www.trylinux.com/projects/udf/>

'm' all linux/mtio.h        conflict!

'm' all linux/soundcard.h   conflict!

'm' all linux/synclink.h    conflict!

'm' 00-1F   net/irda/irmod.h    conflict!

'n' 00-7F   linux/ncp_fs.h

'n' E0-FF   video/matrox.h          matroxfb

'p' 00-3F   linux/mc146818rtc.h

'p' 40-7F   linux/nvram.h

'p' 80-9F               user-spaceparport

                    <mailto:[email protected]>

'q' 00-1F   linux/videotext.h   conflict!

'q' 80-FF               InternetPhoneJACK, Internet LineJACK

                    <http://www.quicknet.net>

'r' 00-1F   linux/msdos_fs.h

's' all linux/cdk.h

't' 00-7F   linux/if_ppp.h

't' 80-8F   linux/isdn_ppp.h

'u' 00-1F   linux/smb_fs.h

'v' 00-1F   linux/ext2_fs.h     conflict!

'v' all linux/videodev.h    conflict!

'w' all             CERNSCI driver

'y' 00-1F               packetbased user level communications

                    <mailto:[email protected]>

'z' 00-3F               CANbus card

                    <mailto:[email protected]>

'z' 40-7F               CANbus card

                    <mailto:[email protected]>

0x80    00-1F   linux/fb.h

0x89    00-06   asm-i386/sockios.h

0x89    0B-DF   linux/sockios.h

0x89    E0-EF   linux/sockios.h     SIOCPROTOPRIVATE range

0x89    F0-FF   linux/sockios.h     SIOCDEVPRIVATE range

0x8B    all linux/wireless.h

0x8C    00-3F               WiNRADiOdriver

                    <http://www.proximity.com.au/~brian/winradio/>

0x90    00  drivers/cdrom/sbpcd.h

0x93    60-7F   linux/auto_fs.h

0x99    00-0F               537-Addinboarddriver

                    <mailto:[email protected]>

0xA0    all linux/sdp/sdp.h     Industrial Device Project

                    <mailto:[email protected]>

0xA2    00-0F  DVD decoder driver      indevelopment:

                                       <http://linuxtv.org/dvd/api/>

0xA3    00-1F   PhilipsSAA7146 dirver  in development:

                    <mailto:Andreas.Beck[email protected]>

0xA3    80-8F   PortACL        in development:

                    <mailto:[email protected]>

0xA3    90-9F   linux/dtlk.h

0xAB    00-1F   linux/nbd.h

0xAC    00-1F   linux/raw.h

0xAD    00  Netfilterdevice    in development:

                    <mailto:[email protected]

0xB0    all RATIOdevices       in development:

                    <mailto:[email protected]>

0xB1    00-1F   PPPoX           <mailto:[email protected]>

0xCB    00-1F   CBMserial IEC bus  in development:

                    <mailto:[email protected]>

0xFE    00-9F   LogicalVolume Manager  <mailto:[email protected]>

其中最左邊的這一列就是所謂的魔數(即幻數)了。

第五篇:Ioctl-----核心空間與使用者空間的資料交換

1. 前言

使用ioctl系統呼叫是使用者空間向核心交換資料的常用方法之一,從ioctl這個名稱上看,本意是針對I/O裝置進行的控制操作,但實際並不限制是真正的I/O裝置,可以是任何一個核心裝置即可。

2. 基本過程

在 核心空間中ioctl是很多核心操作結構的一個成員函式,如檔案操作結構structfile_operations(include/linux/fs.h)、協議操作結構struct proto_ops(include/linux/net.h)等、tty操作結構structtty_driver(include/linux/tty_driver.h)等,而這些操作結構分別對應各種核心裝置,只要在使用者空間開啟這些裝置, 如I/O裝置可用open(2)開啟,網路協議可用socket(2)開啟等,獲取一個檔案描述符後,就可以在這個描述符上呼叫ioctl(2)來向核心交換資料。

3. ioctl(2)

ioctl(2)函式的基本使用格式為:

int ioctl(int fd, int cmd,void *data)

第一個引數是檔案描述符;cmd是操作命令,一般分為GET、SET以及其他型別命令,GET是使用者空間程序從核心讀資料,SET是使用者空間程序向核心寫資料,cmd雖然是一個整數,但是有一定的引數格式的,下面再詳細說明;第三個引數是資料起始位置指標,

cmd命令引數是個32位整數,分為四部分:

dir(2b)  size(14b) type(8b) nr(8b)

詳細定義cmd要包括這4個部分時可使用巨集_IOC(dir,type,nr,size)來定義,而最簡單情況下使用_IO(type,nr)來定義就可以了,這些巨集都在include/asm/ioctl.h中定義

本文cmd定義為:

#defineNEWCHAR_IOC_MAGIC   'M'

#defineNEWCHAR_SET    _IO(NEWCHAR_IOC_MAGIC, 0)

#defineNEWCHAR_GET    _IO(NEWCHAR_IOC_MAGIC, 1)

#define NEWCHAR_IOC_MAXNR   1

要 定義自己的ioctl操作,可以有兩個方式,一種是在現有的核心程式碼中直接新增相關程式碼進行支援,比如想通過socket描述符進行 ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函式中新增自己定義的命令和相關的處理函式,重新編譯核心即可,不過這種方法一般不推薦;第二種方法是定義自己的核心裝置,通過裝置的ioctl()來操作,可以編成模組,這樣不影響原有的核心,這是最通常的做法。

4. 核心裝置

為進行ioctl操作最通常是使用字元裝置來進行,當然定義其他型別的裝置也可以。在使用者空間,可使用mknod命令建立一個字元型別裝置檔案,假設該裝置的主裝置號為123,次裝置號為0:

mknode /dev/newchar c 123 0

如果是程式設計的話,可以用mknode(2)函式來建立裝置檔案。

建立裝置檔案後再將該裝置的核心模組檔案插入核心,就可以使用open(2)開啟/dev/newchar檔案,然後呼叫ioctl(2)來傳遞資料,最後用close(2)關閉裝置。而如果核心中還沒有插入該裝置的模組,open(2)時就會失敗。

由於核心記憶體空間和使用者記憶體空間不同,要將核心資料拷貝到使用者空間,要使用專用拷貝函式copy_to_user();要將使用者空間資料拷貝到核心,要使用copy_from_user()。

要最簡單實現以上功能,核心模組只需要實現裝置的open, ioctl和release三個函式即可,

下面介紹程式片斷:

staticint newchar_ioctl(struct inode *inode, struct file *filep,

unsignedint cmd, unsigned long arg);

static int newchar_open(structinode *inode, struct file *filep);

static intnewchar_release(struct inode *inode, struct file *filep);

// 定義檔案操作結構,結構中其他元素為空

struct file_operationsnewchar_fops =

{

owner:  THIS_MODULE,

ioctl:  newchar_ioctl,

open:  newchar_open,

release: newchar_release,

};

// 定義要傳輸的資料塊結構

struct newchar{

int a;

int b;

};

#define MAJOR_DEV_NUM 123

#define DEVICE_NAME"newchar"

開啟裝置,非常簡單,就是增加模組計數器,防止在開啟裝置的情況下刪除模組,

當然想搞得複雜的話可進行各種限制檢查,如只允許指定的使用者開啟等:

static int newchar_open(structinode *inode, struct file *filep)

{

MOD_INC_USE_COUNT;

 return 0;

}

關閉裝置,也很簡單,減模組計數器:

static intnewchar_release(struct inode *inode, struct file *filep)

{

MOD_DEC_USE_COUNT;

 return 0;

}

進行ioctl呼叫的基本處理函式

static int newchar_ioctl(structinode *inode, struct file *filep,

unsigned int cmd, unsignedlong arg)

{

int  ret;

// 首先檢查cmd是否合法

if(_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;

if(_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;

// 錯誤情況下的預設返回值

ret = EINVAL;

 switch(cmd)

{

case KNEWCHAR_SET:

// 設定操作,將資料從使