* 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;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
char ch;
#endif
/* 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 */
#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
/* Don't reinvent the wheel - call 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:
/* Give the current message to the calling
* process - the parameter we got is a pointer, fill it. */
#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
/* 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;
}
/* Module Declarations *************************** */
/* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions. */
struct file_operations Fops = {
NULL, /* seek */
device_read,
device_write,
NULL, /* readdir */
NULL, /* select */
device_ioctl, /* ioctl */
NULL, /* mmap */
device_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* flush */
#endif
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);
}
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>
/* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know it. */
#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
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;
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_get_nth_byte(file_desc);
ioctl_get_msg(file_desc);
ioctl_set_msg(file_desc, msg);
close(file_desc);
}
ΠΠ°Π³ΡΡΠ·ΠΎΡΠ½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ
ΠΠΎ ΠΌΠ½ΠΎΠ³ΠΈΡ ΠΈΠ· ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠΈΡ ΠΏΡΠΈΠΌΠ΅ΡΠΎΠ², ΠΌΡ ΠΆΠ΅ΡΡΠΊΠΎ Π·Π°Π΄Π°Π²Π°Π»ΠΈ ΠΊΠ°ΠΊΠΈΠ΅-Π»ΠΈΠ±ΠΎ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π² ΠΌΠΎΠ΄ΡΠ»Π΅. ΠΡΠΎ ΠΈΠ΄Π΅Ρ ΠΏΡΠΎΡΠΈΠ² ΠΏΡΠ°Π²ΠΈΠ» Unix ΠΈ Linux, ΡΠΈΠ»ΠΎΡΠΎΡΠΈΡ ΠΊΠΎΡΠΎΡΡΡ ΡΠ°ΠΊΠΎΠ²Π°, ΡΡΠΎ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° Π΄ΠΎΠ»ΠΆΠ½Π° Π±ΡΡΡ Π³ΠΈΠ±ΠΊΠΎΠΉ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΌΠΎΠ³ Π΅Π΅ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°ΡΡ.
Π‘ΠΏΠΎΡΠΎΠ± ΡΠΎΠΎΠ±ΡΠΈΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅ ΠΈΠ»ΠΈ ΠΌΠΎΠ΄ΡΠ»Ρ ΡΡΠΎ-Π»ΠΈΠ±ΠΎ Π΄ΠΎ Π·Π°ΠΏΡΡΠΊΠ° ΡΡΠΎ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ. Π ΡΠ»ΡΡΠ°Π΅ ΡΠ΄Π΅ΡΠ½ΡΡ ΠΌΠΎΠ΄ΡΠ»Π΅ΠΉ, ΠΌΡ Π½Π΅ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ argc ΠΈ argv. ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ, ΠΌΡ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ ΠΊΠΎΠ΅-ΡΡΠΎ Π»ΡΡΡΠ΅. ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΡΡ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΡΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ Π² ΠΌΠΎΠ΄ΡΠ»Π΅, ΠΈ insmod Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ ΠΈΡ Π΄Π»Ρ Π½Π°Ρ.
Π ΡΡΠΎΠΌ ΡΠ΄Π΅ΡΠ½ΠΎΠΌ ΠΌΠΎΠ΄ΡΠ»Π΅ ΠΌΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ Π΄Π²Π΅ ΡΠ°ΠΊΠΈΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ : str1 ΠΈ str2. ΠΡΠ΅, ΡΡΠΎ ΠΡ Π΄ΠΎΠ»ΠΆΠ½Ρ Π΄Π΅Π»Π°ΡΡ ΡΡΠΎ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΠΌΠΎΠ΄ΡΠ»Ρ ΠΈ Π·Π°ΡΠ΅ΠΌ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ insmod str1=xxx str2=yyy. ΠΡΠΈ Π²ΡΠ·ΠΎΠ²Π΅ init_module str1 ΡΠΊΠ°ΠΆΠ΅Ρ Π½Π° ΡΡΡΠΎΠΊΡ `xxx' ΠΈ str2 Π½Π° ΡΡΡΠΎΠΊΡ `yyy'.
Π Π²Π΅ΡΡΠΈΠΈ 2.0 Π½Π΅ ΠΈΠΌΠ΅Π΅ΡΡΡ Π½ΠΈΠΊΠ°ΠΊΠΎΠ³ΠΎ ΠΊΠΎΠ½ΡΡΠΎΠ»Ρ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΡ ΡΠΈΠΏΠΎΠ² Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΎΠ²[6]. ΠΡΠ»ΠΈ ΠΏΠ΅ΡΠ²ΡΠΉ ΡΠΈΠΌΠ²ΠΎΠ» str1 ΠΈΠ»ΠΈ str2 ΡΠ²Π»ΡΠ΅ΡΡΡ ΡΠΈΡΡΠΎΠΉ, ΡΠ΄ΡΠΎ Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ΠΌ ΡΠ΅Π»ΠΎΠ³ΠΎ ΡΠΈΡΠ»Π°, Π° Π½Π΅ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Π΅ΠΌ Π½Π° ΡΡΡΠΎΠΊΡ. Π ΡΠ΅Π°Π»ΡΠ½ΠΎΠΉ ΡΠΈΡΡΠ°ΡΠΈΠΈ ΠΡ Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΏΡΠΎΠ²Π΅ΡΡΡΡ ΡΡΠΎ.
Π‘ Π΄ΡΡΠ³ΠΎΠΉ ΡΡΠΎΡΠΎΠ½Ρ, Π² Π²Π΅ΡΡΠΈΠΈ 2.2 ΠΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΠ΅ ΠΌΠ°ΠΊΡΠΎΠΊΠΎΠΌΠ°Π½Π΄Ρ MACRO_PARM , ΡΡΠΎΠ±Ρ ΡΠΎΠΎΠ±ΡΠΈΡΡ insmod, ΡΡΠΎ ΠΡ ΠΎΠΆΠΈΠ΄Π°Π΅ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ, ΠΈΡ ΠΈΠΌΠ΅Π½Π° ΠΈ ΠΈΡ ΡΠΈΠΏΡ. ΠΡΠΎ ΡΠ΅ΡΠ°Π΅Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΡΠΈΠΏΠ° ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΌΠΎΠ΄ΡΠ»ΡΠΌ ΠΏΠΎΠ»ΡΡΠ°ΡΡ ΡΡΡΠΎΠΊΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ Π½Π°ΡΠΈΠ½Π°ΡΡΡΡ Ρ ΡΠΈΡΡΡ.
param.c/* param.c
*
* Receive command line parameters at module installation
*/
/* Copyright (C) 1998-99 by Ori Pomerantz */
/* The necessary header files */
/* 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 <stdio.h> /* I need NULL */
/* In 2.2.3 /usr/include/linux/version.h includes a