Linux Kernel programming (dialog with device files)

Source: Internet
Author: User
Linux Kernel programming (dialog with Device Files)-General Linux technology-Linux programming and kernel information. For details, see the following. Device files are used to represent physical devices. Most physical devices are used for output or input. Therefore, the device driver in the kernel must get output from the process to the device. This can be done by opening the output device file and writing it, and you want to write a common file. In the following example, this is implemented by device_write.
This is not always effective. Imagine that you are connected to a serial port of the modem (the technology is that you have an inner cat, from the CPU point of view it is also implemented as a serial port, so you do not need to think this idea is too difficult ). The most natural thing to do is to use the device file to write the content to the modem (whether using the modem command or telephone line) or read the information from the modem (you can also use the modem command to answer or use the telephone line ). But the question behind this is what needs to be done when you need to talk to the serial port itself? For example, the rate at which data is sent and received.
The answer is that Unix uses a special function called ioctl (short for input output control. Each device has its own ioctl command, which can be read, written, or both. The Ioctl function is called by three parameters: The description sub-, the number of ioctl, and a long integer parameter of the appropriate device. You can assign a role to pass everything.
The Ioctl number is used to encode the device master code, ioctl type, encoding, and parameter type. The Ioctl count is usually called by a macro in the header file (_ IO, _ IOR, _ IOW or _ IOWR -- depends on the type ). This header file must be included in the program and kernel module (so they can generate the correct ioctl's) using ioctl. In the following example, the header file is chardev. h and its program is ioctl. c.
If you want to use ioctl's in your own kernel module, you 'd better accept a formal ioctl position so that you can get others' ioctl's or they will get you, then you can know what went wrong. If you want more information, go to 'documentation/ioctl-number.txt 'to view the kernel source file tree.
Ex chardev. c

/* Chardev. c
*
* Create an input/output character device
*/


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



/* The necessary header files */

/* Standard in kernel modules */
# Include/* Were doing kernel work */
# Include/* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */
# If CONFIG_MODVERSIONS = 1
# Define MODVERSIONS
# Include
# Endif

/* For character devices */

/* The character device definitions are here */
# Include

/* A wrapper which does next to nothing
* At present, but may help for compatibility
* With future versions of Linux */
# Include


/* Our own ioctl numbers */
# Include "chardev. h"


/* In 2.2.3/usr/include/linux/version. h between des
* Macro for this, but 2.0.35 doesnt-so I add it
* Here if necessary .*/
# Ifndef KERNEL_VERSION
# Define KERNEL_VERSION (a, B, c) (a) x 65536 + (B) * 256 + (c ))
# Endif



# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
# Include/* for get_user and put_user */
# Endif



# Define SUCCESS 0


/* Device Declarations *********************************/


/* The name for our device, as it will appear in
*/Proc/devices */
# Define DEVICE_NAME "char_dev"


/* The maximum length of the message for the device */
# Define BUF_LEN 80

/* Is the device open right now? Used to prevent
* Concurent access into the same device */
Static int Device_Open = 0;

/* The message the device will give when asked */
Static char Message [BUF_LEN];

/* How far did the process reading the message get?
* Useful if the message is larger than the size of
* Buffer we get to fill in device_read .*/
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 dont want to talk to two processes at
* Same time */
If (Device_Open)
Return-EBUSY;

/* If this was a process, we wowould 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, were in
* Kernel, so were protected against context switches.
*
* This is NOT the right attitude to take, because we
* Might be running on an SMP box, but well deal
* 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
* Device file. It doesnt have a return value because
* It cannot fail. Regardless of what else happens, you
* Shoshould always be able to close a device (in 2.0, a 2.2
* Device file cocould be impossible to close ).*/
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Static int device_release (struct inode * inode,
Struct file * file)
# Else
Static void device_release (struct inode * inode,
Struct file * file)
# Endif
{
# Ifdef DEBUG
Printk ("device_release (% p, % p) \ n", inode, file );
# Endif

/* Were now ready for our next caller */
Device_Open --;

MOD_DEC_USE_COUNT;

# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Return 0;
# Endif
}



/* This function is called whenever a process which
* Has already opened the device file attempts
* Read from it .*/
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
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 */
# Else
Static int device_read (
Struct inode * inode,
Struct file * file,
Char * buffer,/* The buffer to fill with the data */
Int length)/* The length of the buffer
* (Mustnt write beyond that !) */
# Endif
{
/* 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 were 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 wouldnt
* Work. Instead, we have to use put_user which
* Copies data from the kernel data segment to
* 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
* Write into our device file .*/
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Static ssize_t device_write (struct file * file,
Const char * buffer,
Size_t length,
Loff_t * offset)
# Else
Static int device_write (struct inode * inode,
Struct file * file,
Const char * buffer,
Int length)
# Endif
{
Int I;

# Ifdef DEBUG
Printk ("device_write (% p, % s, % d )",
File, buffer, length );
# Endif

For (I = 0; I
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Get_user (Message , Buffer + I );
# Else
Message= Get_user (buffer + I );
# Endif

Message_Ptr = Message;

/* Again, return the number of input characters used */
Return I;
}


/* This function is called whenever a process tries
* 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
* 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;
# 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 devices 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 & ibr temp ++) I ++,> get_user (ch, temp );
# Else
For (I = 0; get_user (temp) & ibr temp ++) I ++,>;
# Endif

/* Dont 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 its 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 (\, (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 cant be local
* 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 ("youll 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 thats the \ n ");
Printk ("file youll 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 theres an error, report it */
If (ret <0)
Printk ("Error in module_unregister_chrdev: % d \ n", ret );
}
Ex 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



/* The major device number. We cant 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 were 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 were using.
*
* The second argument is the number of the command
* (There cocould 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
* Buffer to place the message in to be input,
* As it is allocated by the process.
*/


/* Get the nth byte of the message */
# Define IOCTL_GET_NTH_BYTE _ IOWR (MAJOR_NUM, 2, int)
/* The IOCTL is used for both input and output. It
* Es from the user a number, n, and returns
* Message [n]. */


/* The name of the device file */
# Define DEVICE_FILE_NAME "char_dev"


# Endif

Ex ioctl. c

/* Ioctl. c-the process to use ioctls to control
* Kernel module
*
* Until now we cocould have used cat for input and
* Output. But now we need to do ioctls, which require
* Writing our own process.
*/

/* Copy right (C) 1998 by Ori Pomerantz */


/* Device specifics, such as ioctl numbers and
* Major device file .*/
# Include "chardev. h"


# Include/* open */
# Include/* exit */
# Include/* ioctl */



/* Functions for the ioctl cballs */

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 dont tell
* The kernel how far its allowed to write, so it
* Might overflow the buffer. In a real production
* Program, we wocould 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 % dth 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 ("Cant 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 );
}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.