close

原文出自:http://nano-chicken.blogspot.tw/2009/12/linux-modulesv-ioctl.html

 (V)將介紹file operations中的ioctl。ioctl的prototype為:
int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
ioctl藉由cmd來判斷後面所接的參數為何,而早期的ioctl號碼並沒有規則,所以很容易重複,後來為了避免重複,採行編碼方式,將cmd拆成幾個部份,包含:
type
  即magic number,可以根據Document/ioctl/ioctl-number.txt挑選一個。
number
  為sequential number或者稱為ordinal number,讓user自行定義,只要自己不重複即可。
direction
  傳輸的方向,不外乎NONOE/READ/WRITE等等。
size
  即參數的size。

因為ioctl藉由cmd來判斷user想要的指令為何,以及後面所帶的參數為何,所以免不了的就會有一個switch/case來判斷,這也算是ioctl的特色吧。
怎麼定義ioctl的command以及如何解譯ioctl的command,我想直接拿ioctl.h來說明。

#define _IOC(dir,type,nr,size) \
     (((dir) > _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)      (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)       (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)      (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
在定義ioctl的command時,我們會根據資料傳輸的方向使用_IO(不需要傳輸資料)/_IOR(讀取)/_IOW(寫入)/_IOWR(讀寫),type是我們挑選的magic number,nr即number,是流水號,size,就是the size of argument,下面是我們的範例brook_ioctl.h:

#ifndef IOC_BROOK_H
#define IOC_BROOK_H

#define BROOK_IOC_MAGIC   'k'
#define BROOK_IOCSETNUM   _IOW(BROOK_IOC_MAGIC, 1, int)
#define BROOK_IOCGETNUM   _IOR(BROOK_IOC_MAGIC, 2, int)
#define BROOK_IOCXNUM    _IOWR(BROOK_IOC_MAGIC, 3, int)
#define BROOK_IOC_MAXNR   3

#endif
這邊定義三個ioctl的command,分別為設定數值(BROOK_IOCSETNUM),取得數值(BROOK_IOCGETNUM)和交換數值(BROOK_IOCXNUM)。

以下是我的module:
#include 
#include 

#include  // chrdev
#include  // cdev_add()/cdev_del()
#include  // up()/down_interruptible()
#include  // copy_*_user()

#include "ioc_brook.h"

MODULE_LICENSE("GPL");

#define DEV_BUFSIZE     1024


static int dev_major;
static int dev_minor;
struct cdev *dev_cdevp = NULL;

static int 
 dev_open(struct inode*, struct file*);
static int 
 dev_release(struct inode*, struct file*);
static int
 dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);

static void __exit exit_modules(void);

struct file_operations dev_fops = {
  .owner  = THIS_MODULE,
  .open  = dev_open,
  .release = dev_release,
  .ioctl  = dev_ioctl
};

static int dev_open(struct inode *inode, struct file *filp)
{
  printk("%s():\n", __FUNCTION__);
  return 0;
}

static int dev_release(struct inode *inode, struct file *filp)
{
  printk("%s():\n", __FUNCTION__);
  return 0;
}

static int brook_num = 0;
static int 
dev_ioctl(struct inode *inode, struct file *filp,
     unsigned int cmd, unsigned long args)
{
  int tmp, err = 0, ret = 0;

  if (_IOC_TYPE(cmd) != BROOK_IOC_MAGIC)
    return -ENOTTY;
  if (_IOC_NR(cmd) > BROOK_IOC_MAXNR)
    return -ENOTTY;

  if (_IOC_DIR(cmd) & _IOC_READ) {
    err = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd));
  } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
    err = !access_ok(VERIFY_READ, (void __user *)args, _IOC_SIZE(cmd));
  }
  if (err)
    return -EFAULT;

  switch (cmd) {
    case BROOK_IOCSETNUM:
      // don't need call access_ok() again. using __get_user().
      ret = __get_user(brook_num, (int __user *)args); 
      printk("%s(): get val = %d\n", __FUNCTION__, brook_num);
      break;
    case BROOK_IOCGETNUM:
      ret = __put_user(brook_num, (int __user *)args);
      printk("%s(): set val to %d\n", __FUNCTION__, brook_num);
      break;
    case BROOK_IOCXNUM:
      tmp = brook_num;
      ret = __get_user(brook_num, (int __user *)args);
      if (!ret) {
        ret = __put_user(tmp, (int __user *)args);
      }
      printk("%s(): change val from %d to %d\n",
            __FUNCTION__, tmp, brook_num);
      break;
    default: /* redundant, as cmd was checked against MAXNR */
      return -ENOTTY;
  }
  return 0;
}

static int __init init_modules(void)
{
  dev_t dev;
  int ret;

  ret = alloc_chrdev_region(&dev, 0, 1, "brook");
  if (ret owner = THIS_MODULE;
  ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1);
  if (ret 在dev_ioctl()先檢視command的type(magic number)和number(sequential number)是否正確,接著在根據command的read/write特性,使用access_ok()檢驗該位址是否合法,後面就是ioctl慣有的switch/case了,根據不同的case執行不同的command和解釋後面所攜帶的參數。

底下是我的application:
#include 
#include 
#include 

#include 
#include 

#include "ioc_brook.h"

int main(int argc, char *argv[])
{
  int fd, ret;

  if (argc 在app.c中,將開啟上面註冊的device,並且設定數值(BROOK_IOCSETNUM),讀取數值(BROOK_IOCGETNUM),和交換數值(BROOK_IOCXNUM)。


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 angledark0123 的頭像
    angledark0123

    CONY的世界

    angledark0123 發表在 痞客邦 留言(0) 人氣()