📄 和设备文件对话(写和 ioctl).htm
字号:
/* 得到由进程给出的给 ioctl 的参数 */
temp = (char *) ioctl_param;
/* 找到消息的长度 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
get_user(ch, temp);
for (i=0; ch && i<BUF_LEN; i++, temp++)
get_user(ch, temp);
#else
for (i=0; get_user(temp) && i<BUF_LEN; i++, temp++)
;
#endif
/* 不要重新发明车轮-调用 device_write */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
device_write(file, (char *) ioctl_param, i, 0);
#else
device_write(inode, file, (char *) ioctl_param, i);
#endif
break;
case IOCTL_GET_MSG:
/* 将当前的消息给调用进程 - 参数是一个指针,填充它 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
i = device_read(file, (char *) ioctl_param, 99, 0);
#else
i = device_read(inode, file, (char *) ioctl_param,
99);
#endif
/* 警告 - 我们假设缓冲区长度是100。如果它小于那将使缓冲区溢出而导致进程倾倒核心
*(生成core文件)。
*
* 我们只允许99个字符的原因是字符串终止符 NULL 也需要空间。 */
/* 将0放置在缓冲区尾使它适当的终止。 */
put_user('\0', (char *) ioctl_param+i);
break;
case IOCTL_GET_NTH_BYTE:
/* 这个 ioctl 既输入 (ioctl_param) 也输出(这个函数的返回值) */
return Message[ioctl_param];
break;
}
return SUCCESS;
}
/* 模块声明 *************************** */
/* 这个结构将保存当进程对我们创建的设备做什么时将调用的函数。因为这个结构的指针被保存在
* 设备表中,所以它不能对 init_module是局部的。 NULL 是为未实现的函数保留的。 */
struct file_operations Fops = {
NULL, /* 偏移 */
device_read,
device_write,
NULL, /* 读目录 */
NULL, /* 选择 */
device_ioctl, /* ioctl */
NULL, /* mmap */
device_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* 刷新 */
#endif
device_release /* 又名关闭 */
};
/* 初始化模块 - 登记字符设备 */
int init_module()
{
int ret_val;
/* 登记字符设备(至少是试图登记) */
ret_val = module_register_chrdev(MAJOR_NUM,
DEVICE_NAME,
&Fops);
/* 负数表示错误 */
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;
}
/* 清除 - 从 /proc 中注销相关文件 */
void cleanup_module()
{
int ret;
/* 注销设备 */
ret = module_unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
/* 如果有错误,报告它 */
if (ret < 0)
printk("Error in module_unregister_chrdev: %d\n", ret);
}
</PRE>
<P>范例 <FONT size=+1><B>chardev.h</B></FONT> <A name=442> </A><A
name=443> </A>
<P><PRE>
/* chardev.h - 包含 ioctl 定义的头文件。
*
* 这里的声明必须在头文件中,因为他们需要被内核模块(chardev.c)和调用它的进程 (ioctl.c)知道。
*/
#ifndef CHARDEV_H
#define CHARDEV_H
#include <linux/ioctl.h>
/* 主设备号。我们不能再依赖于动态的登记,因为 ioctl 需要知道它。 */
#define MAJOR_NUM 100
/* 设置设备驱动程序的消息 */
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
/* _IOR 意思是我们正在为从用户进程到内核模块的信息创建一个 ioctl 命令号。
*
* 第一个参数, MAJOR_NUM,是我们使用的主设备号。
*
* 第二个参数是命令号(可能是几个带有不同的意思的)。
*
* 第三个参数是我们想得到的从进程传到内核的类型。
*/
/* 得到设备驱动程序的消息 */
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
/* 这个 IOCTL 用于输出,得到设备驱动程序的消息。然而我们仍然需要缓冲区作为输入放置消息,因为
* 它已经被进程分配了。
*/
/* 得到消息的第n个字节 */
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
/* 这个 IOCTL 既用于输入也用于输出。它接受来自用户的一个数 n ,然后返回 Message[n]。 */
/* 设备文件名 */
#define DEVICE_FILE_NAME "char_dev"
#endif
</PRE>
<P><A name=446> </A> <A name=447> </A> <A name=448> </A> <A
name=449> </A>
<P>范例 <FONT size=+1><B>ioctl.c</B></FONT> <A name=454> </A><A
name=455> </A>
<P><PRE>
/* ioctl.c - 进程使用 ioctl 去控制内核模块
*
* 直到现在我们都使用 cat 做输入和输出。但是现在我们需要做 ioctl ,这需要写自己的进程。
*/
/* Copyright (C) 1998 by Ori Pomerantz */
/* 设备细节,例如 ioctl号和主设备号 */
#include "chardev.h"
#include <fcntl.h> /* 打开 */
#include <unistd.h> /* 退出 */
#include <sys/ioctl.h> /* ioctl */
/* ioctl 调用函数 */
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];
/* 警告 - 这是危险的,因为我们没有告诉内核它允许写多远,因此它可能使缓冲区溢出。
* 在真正的产品程序中,我们应该使用两个 ioctl - 一个告诉内核缓冲区长度而另一个填充缓冲区
*/
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;
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');
}
/* 主函数 - 调用 ioctl 函数 */
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_get_nth_byte(file_desc);
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc, msg);
close(file_desc);
}
</PRE>
<P><A name=458></A>
<P>
<HR>
<!--Navigation Panel--><A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node19.html"
name=tex2html599><IMG height=24 alt=next
src="和设备文件对话(写和 IOCTL).files/next_motif.gif" width=37 align=bottom border=0></A>
<A href="http://yangxingjun.myrice.com/chinesehow/kernel/mpg.html"
name=tex2html595><IMG height=24 alt=up src="和设备文件对话(写和 IOCTL).files/error.htm"
width=26 align=bottom border=0></A> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node17.html"
name=tex2html589><IMG height=24 alt=previous
src="C:\Documents and Settings\nonoka\My Documents\和设备文件对话(写和 IOCTL).files\error(1).htm"
width=63 align=bottom border=0></A> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node1.html"
name=tex2html597><IMG height=24 alt=contents
src="和设备文件对话(写和 IOCTL).files/contents_motif.gif" width=65 align=bottom
border=0></A> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node34.html"
name=tex2html598><IMG height=24 alt=index
src="和设备文件对话(写和 IOCTL).files/index_motif.gif" width=43 align=bottom
border=0></A> <BR><B>Next:</B> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node19.html"
name=tex2html600>启动参数</A> <B>Up:</B> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/mpg.html"
name=tex2html596>Linux 内核模块编程</A> <B>Previous:</B> <A
href="http://yangxingjun.myrice.com/chinesehow/kernel/node17.html"
name=tex2html590>将 /proc 作为输入</A> <!--End of Navigation Panel-->
<ADDRESS><I></I><BR><I>1999-05-19</I> </ADDRESS></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -