Linux字符設備驅動入門(二)——加入ioctl功能

平臺:VMware 7.0 + Linux ubuntu 3.0.0-12-generic

編譯器:gcc

參考資料:LDD 3

功能:實現ioctl功能

          在Linux字符設備驅動入門(一)中,我們實現了字符設備的簡單讀寫字符功能,接下來我們要在這個基礎上加入ioctl功能。首先,我們先來看看3.0內核下../include/linux/fs.h中file_operations結構體的定義:


struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
};

      紅色字體已經標出在kernel 3.0中已經完全刪除了struct file_operations 中的ioctl 函數指針,剩下unlocked_ioctlcompat_ioctl,取而代之的是unlocked_ioctl,主要改進就是不再需要上大內核鎖 (調用之前不再先調用lock_kernel()然後再unlock_kernel())。

       所以,在hellow.c中,我們在file_operations中加入成員函數hello_ioctl(紅色字體部分):

/* file operations for hello device */
static struct file_operations hello_ops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = hello_ioctl,
    .open = hello_open,
    .read = hello_read,
    .write = hello_write,
    .release = hello_release,
};

hello_ioctl()的定義如下:

static int hello_ioctl( struct file *file,
            unsigned int cmd, unsigned long arg)
{    int temp = 0;
    switch(cmd)
    {
      case HELLO_CMD1:
               {
            temp = 1;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) return -EFAULT;
            break;
               }
      case HELLO_CMD2:
            {
            temp = 2;
            if(copy_to_user( (int *)arg, &temp, sizeof(int))) return -EFAULT;
            break;
            }
    }
    printk( KERN_NOTICE"ioctl CMD%d done!\n",temp);    

return 0;

}


       這裏強調一下cmd的定義:

#define HELLO_MAGIC 'k'
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

            其中'k'爲幻數,要按照Linux內核的約定方法爲驅動程序選擇ioctl編號,應該首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt這兩個文件,下面是ioctl.h的部分內容,也是比較重要的:

_IO(type, nr)
       用於構造無參數的命令編號;
_IOR(type, nr, datatype)
       用於構造從驅動程序中讀取數據的命令編號;
_IOW(type, nr, datatype)
       用於寫入數據的命令;
_IOWR(type, nr, datatype)
       用於雙向傳輸。
注意千萬不能重複定義。


注意對幻數的編號千萬不能重複定義,如ioctl-number.txt已經說明‘k'的編號已經被佔用的範圍爲:

'k'    00-0F    linux/spi/spidev.h    conflict!
'k'    00-05    video/kyro.h        conflict!

     所以我們在這裏分別編號爲0x1a和0x1b,到這裏,我們已經完成了對ioctl功能的編寫,接下來就是在測試程序中利用系統調用來測試它。

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

ioctl測試程序

=============================================================
#include <stdio.h>  
#include <fcntl.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define HELLO_MAGIC 'k'  //當然我們也可以定義一個相應的頭文件,把ioctl的cmd放進裏面,然後再include進 來
#define HELLO_CMD1    _IO(HELLO_MAGIC,0x1a)
#define HELLO_CMD2    _IO(HELLO_MAGIC,0x1b)

      int main(void)
{
    int ioctl_rdata;
    int fd, ret;

    fd = open ( "/dev/hellow" , O_RDWR);
    if ( fd == -1 )
    {
      perror("open");
      exit(0);
        }

    ret = ioctl( fd, HELLO_CMD2,&ioctl_rdata);
    if ( ret == -1)
    {
     perror("ioctl");
     exit(0);
    }
    printf("ioctl_rdata= %d \n",ioctl_rdata);

    close(fd);
    return 0;
}
=============================================================

運行結果

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

root@ubuntu:~/share/hellow# insmod hellow.ko
root@ubuntu:~/share/hellow# mknod /dev/hellow c 251 0
root@ubuntu:~/share/hellow# ./a.out
ioctl_rdata= 2
root@ubuntu:~/share/hellow# dmesg | tail

[ 2431.126532] hello init. major:251, minor:0
[ 2453.326022] Hello device open!

[ 2453.326047] ioctl CMD2 done!
[ 2453.326487] Hello device close!




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章