裝置檔案“交談”(writes 和 IOCTLs)

來源:互聯網
上載者:User

 第六章  和裝置檔案“交談”(writes 和 IOCTLs)

    裝置檔案假定是對應於物理裝置的。大多數的物理裝置既可以輸入又可以輸
出。所以核心中的裝置驅動程式應該有某些機制來取得進程的輸出並送到裝置。
這可以通過用輸出的方式開啟裝置檔案並向它寫入來作到,就好象寫一個檔案。
下面的例子是用device_write來實現。

    這通常還是不夠的。假設你有一個串口連到modem上(即使你有內建modem,從
CPU的角度看,它仍然要被實現為串口和modem串連,但物理上並沒有)。自然的方
式就是用裝置檔案來向modem寫(modem命令或資料通過電話線傳送)和從modem讀入
資料(命令的回應或資料的接收也是通過電話線)。當你需要和串口自身通訊時會
出現問題,例如設定資料傳送和接收的速率。

    Unix下的答案是使用專用的ioctl函數(input output control的縮寫)。每個
裝置都有自身的ioctl命令,既可以讀ioctl's(從進程向核心發資料),又可以寫
ioctl's(返回資訊到進程)。ioctl函數用三個參數調用:裝置檔案描述符、ioct
l號和一個long型的參數(你可以通過它傳送任何資訊)。

   

    ioctl號包括主裝置號、ioctl類型(命令)和參數類型。ioctl號一般用標頭檔
裡的宏來產生(_IO, _IOR, _IOW 或 _IORW,取決於類型)。這個標頭檔應該被使
用ioctl系統調用的程式包含,也應該被實現ioctl的核心模組包含。本例中,這
個標頭檔是chardev.h,它被chardev.c(核心模組的實現)和ioctl.c使用(調用io
ctl)。

    如果你想在你的核心模組中使用ioctl,最好用一個官方指定的ioctl約定。
可以參考/usr/src/linux/Documentation/ioctl-number.txt檔案。

例子chardev.h

/* chardev.h - the header file with the ioctl definitions.

 * The declarations here have to be in a header file, 

 * because they need to be known both to the kernel 

 * module (in chardev.c) and the process calling ioctl 

 * (ioctl.c) */

#ifndef CHARDEV_H

#define CHARDEV_H

#include <linux/ioctl.h> 

/* 主裝置號,你不能依賴動態分配的主裝置號了,

   因為ioctl要用到*/

#define MAJOR_NUM 100

/* Set the message of the device driver */

#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)

/* _IOR means that we're creating an ioctl command 

 * number for passing information from a user process

 * to the kernel module. 

 *

 * The first arguments, MAJOR_NUM, is the major device 

 * number we're using.

 *

 * The second argument is the number of the command 

 * (there could be several with different meanings).

 *

 * The third argument is the type we want to get from 

 * the process to the kernel.

 */

/* Get the message of the device driver */

#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)

 /* This IOCTL is used for output, to get the message 

  * of the device driver. However, we still need the 

  * buffer to place the message in to be input, 

  * as it is allocated by the process.  */

/* Get the n'th byte of the message */

#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)

 /* The IOCTL is used for both input and output. It 

  * receives from the user a number, n, and returns 

  * Message[n]. */

/* The name of the device file */

#define DEVICE_FILE_NAME "char_dev"

#endif

/*-----------end of chardev.h-------------*/

例子chardev.c    

 

/* chardev.c 

 * 

 * Create an input/output character device

 */

/* Copyright (C) 1998-99 by Ori Pomerantz */

/* Standard in kernel modules */

#include <linux/kernel.h>   /* We're doing kernel work */

#include <linux/module.h>   /* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include <linux/modversions.h>

#endif        

#include <linux/fs.h>       

#include <linux/wrapper.h>  

/* Our own ioctl numbers */

#include "chardev.h"

#include <asm/uaccess.h>  /* for get_user and put_user */

#define SUCCESS 0

/* The name for our device, as it will appear in 

 * /proc/devices */

#define DEVICE_NAME "char_dev"

#define BUF_LEN 80

static int Device_Open = 0;

static char Message[BUF_LEN];

static char *Message_Ptr;

/* This function is called whenever a process attempts 

 * to open the device file */

static int device_open(struct inode *inode, 

                       struct file *file)

{

#ifdef DEBUG

  printk ("device_open(%p)/n", file);

#endif

  /* We don't want to talk to two processes at the 

   * same time */

  if (Device_Open)

    return -EBUSY;

  /* If this was a process, we would have had to be 

   * more careful here, because one process might have 

   * checked Device_Open right before the other one 

   * tried to increment it. However, we're in the 

   * kernel, so we're protected against context switches.

   *

   * This is NOT the right attitude to take, because we

   * might be running on an SMP box, but we'll deal with

   * SMP in a later chapter.

   */ 

  Device_Open++;

  /* Initialize the message */

  Message_Ptr = Message;

  MOD_INC_USE_COUNT;

  return SUCCESS;

}

/* This function is called when a process closes the 

 * device file. It doesn't have a return value because 

 * it cannot fail. Regardless of what else happens, you 

 * should always be able to close a device (in 2.0, a 2.2

 * device file could be impossible to close). */

static int device_release(struct inode *inode, 

                          struct file *file)

{

#ifdef DEBUG

  printk ("device_release(%p,%p)/n", inode, file);

#endif

  /* We're now ready for our next caller */

  Device_Open --;

  MOD_DEC_USE_COUNT;

  return 0;

}

/* This function is called whenever a process which 

 * has already opened the device file attempts to 

 * read from it. */

static ssize_t device_read(

    struct file *file,

    char *buffer, /* The buffer to fill with the data */   

    size_t length,     /* The length of the buffer */

    loff_t *offset) /* offset to the file */

{

  /* Number of bytes actually written to the buffer */

  int bytes_read = 0;

#ifdef DEBUG

  printk("device_read(%p,%p,%d)/n",

    file, buffer, length);

#endif

  /* If we're at the end of the message, return 0 

   * (which signifies end of file) */

  if (*Message_Ptr == 0)

    return 0;

  /* Actually put the data into the buffer */

  while (length && *Message_Ptr)  {

    /* Because the buffer is in the user data segment, 

     * not the kernel data segment, assignment wouldn't 

     * work. Instead, we have to use put_user which 

     * copies data from the kernel data segment to the 

     * user data segment. */

    put_user(*(Message_Ptr++), buffer++);

    length --;

    bytes_read ++;

  }

#ifdef DEBUG

   printk ("Read %d bytes, %d left/n",

     bytes_read, length);

#endif

   /* Read functions are supposed to return the number 

    * of bytes actually inserted into the buffer */

  return bytes_read;

}

/* This function is called when somebody tries to 

 * write into our device file. */ 

static ssize_t device_write(struct file *file,

                            const char *buffer,

                            size_t length,

                            loff_t *offset)

{

  int i;

#ifdef DEBUG

  printk ("device_write(%p,%s,%d)",

    file, buffer, length);

#endif

  for(i=0; i<length && i<BUF_LEN; i++)

get_user(Message[i], buffer+i);

Message_Ptr = Message;

/* Again, return the number of input characters used */

return i;

}

/* This function is called whenever a process tries to

* do an ioctl on our device file. We get two extra

* parameters (additional to the inode and file

* structures, which all device functions get): the number

* of the ioctl called and the parameter given to the

* ioctl function.

*

* If the ioctl is write or read/write (meaning output

* is returned to the calling process), the ioctl call

* returns the output of this function.

*/

int device_ioctl(

struct inode *inode,

struct file *file,

unsigned int ioctl_num,/* The number of the ioctl */

unsigned long ioctl_param) /* The parameter to it */

{

int i;

char *temp;

char ch;

/* Switch according to the ioctl called */

switch (ioctl_num) {

case IOCTL_SET_MSG:

/* Receive a pointer to a message (in user space)

* and set that to be the device's message. */

/* Get the parameter given to ioctl by the process */

temp = (char *) ioctl_param;

/* Find the length of the message */

get_user(ch, temp);

for (i=0; ch && i<BUF_LEN; i++, temp++)

get_user(ch, temp);

/* Don't reinvent the wheel - call device_write */

device_write(file, (char *) ioctl_param, i, 0);

break;

case IOCTL_GET_MSG:

/* Give the current message to the calling

* process - the parameter we got is a pointer,

* fill it. */

i = device_read(file, (char *) ioctl_param, 99, 0);

/* Warning - we assume here the buffer length is

* 100. If it's less than that we might overflow

* the buffer, causing the process to core dump.

* The reason we only allow up to 99 characters is

* that the NULL which terminates the string also

* needs room. */

/* Put a zero at the end of the buffer, so it

* will be properly terminated */

put_user('/0', (char *) ioctl_param+i);

break;

case IOCTL_GET_NTH_BYTE:

/* This ioctl is both input (ioctl_param) and

* output (the return value of this function) */

return Message[ioctl_param];

break;

}

return SUCCESS;

}

struct file_operations Fops = {

NULL, /* seek */

device_read,

device_write,

NULL, /* readdir */

NULL, /* select */

device_ioctl, /* ioctl */

NULL, /* mmap */

device_open,

NULL, /* flush */

device_release /* a.k.a. close */

};

/* Initialize the module - Register the character device */

int init_module()

{

int ret_val;

/* Register the character device (atleast try) */

ret_val = module_register_chrdev(MAJOR_NUM,

DEVICE_NAME,

&Fops);

/* Negative values signify an error */

if (ret_val < 0) {

printk ("%s failed with %d/n",

"Sorry, registering the character device ",

ret_val);

return ret_val;

}

printk ("%s The major device number is %d./n",

"Registeration is a success",

MAJOR_NUM);

printk ("If you want to talk to the device driver,/n");

printk ("you'll have to create a device file. /n");

printk ("We suggest you use:/n");

printk ("mknod %s c %d 0/n", DEVICE_FILE_NAME,

MAJOR_NUM);

printk ("The device file name is important, because/n");

printk ("the ioctl program assumes that's the/n");

printk ("file you'll use./n");

return 0;

}

/* Cleanup - unregister the appropriate file from /proc */

void cleanup_module()

{

int ret;

/* Unregister the device */

ret = module_unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

/* If there's an error, report it */

if (ret < 0)

printk("Error in module_unregister_chrdev: %d/n", ret);

}

/*------- end of chardev.c -----------*/

用於測試ioctl的例子ioctl.c

/* ioctl.c - the process to use ioctl's to control the

* kernel module

*

* Until now we could have used cat for input and

* output. But now we need to do ioctl's, which require

* writing our own process.

*/

/* Copyright (C) 1998 by Ori Pomerantz */

/* device specifics, such as ioctl numbers and the

* major device file. */

#include "chardev.h"

#include <fcntl.h>      /* open */ 

#include <unistd.h>     /* exit */

#include <sys/ioctl.h>  /* ioctl */

/* Functions for the ioctl calls */

ioctl_set_msg(int file_desc, char *message)

{

  int ret_val;

  ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

  if (ret_val < 0) {

printf ("ioctl_set_msg failed:%d/n", ret_val);

exit(-1);

}

}

ioctl_get_msg(int file_desc)

{

int ret_val;

char message[100];

/* Warning - this is dangerous because we don't tell

* the kernel how far it's allowed to write, so it

* might overflow the buffer. In a real production

* program, we would have used two ioctls - one to tell

* the kernel the buffer length and another to give

* it the buffer to fill

*/

ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

if (ret_val < 0) {

printf ("ioctl_get_msg failed:%d/n", ret_val);

exit(-1);

}

printf("get_msg message:%s/n", message);

}

ioctl_get_nth_byte(int file_desc)

{

int i;

char c=1;

printf("get_nth_byte message:");

i = 0;

while (c != 0) {

c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);

if (c < 0) {

printf(

"ioctl_get_nth_byte failed at the %d'th byte:/n", i);

exit(-1);

}

putchar(c);

}

putchar('/n');

}

/* Main - Call the ioctl functions */

main()

{

int file_desc, ret_val;

char *msg = "Message passed by ioctl/n";

file_desc = open(DEVICE_FILE_NAME, 0);

if (file_desc < 0) {

printf ("Can't open device file: %s/n",

DEVICE_FILE_NAME);

exit(-1);

}

ioctl_set_msg(file_desc, msg);

ioctl_get_nth_byte(file_desc);

ioctl_get_msg(file_desc);

close(file_desc);

}

編譯和測試:

1、編譯

cc -D__KERNEL__ -DMODULE -DLINUX -DDEBUG -O6 -c chardev.c

cc ioctl.c -o ~/bin/ioctl

su to root:

mknod char_dev c 100 0

kevintz註:總覺得作者寫的ioctl.c不是很好,我已經作了小小的修改,已經能
夠合理輸出結果。

2、測試

insmod後,看看/proc/modules /proc/devices的變化。運行ioctl進行測試,看
看結果和核心模組的輸出。

--
那一刹那,我開始用心去看這個世界,所有的事物真的可以看得前

所未有的那麼清楚……

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.