- Ioctl
- Supported operations, such as
- Simple Data transfer
- Control actions, such as user space initiating pop-up media actions
- Feedback the status of your hardware, such as reporting error messages
- Parameter configuration, such as changing the baud rate
- Perform self-sabotage
- The IOCTL method prototype for user space: int ioctl (int fd, unsigned long cmd, ...); Each IOCTL command is a separate system call, and it is a non-public
- The driver's IOCTL method prototype: Int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
- Select the IOCTL command
- To facilitate the programmer to create a unique IOCTL command number, each command number is divided into multiple bit fields
- Contract method for the Linux kernel select the IOCTL number for the driver
- Include/asm/ioctl.h
- Defines the bit fields to use
- Type (magic number)
- Ordinal
- Transmission direction
- Parameter size
- Documentation/ioctl-number.txt
- Lists the magic numbers used by the kernel
- <linux/ioctl.h>
- Type
- Magic number, this field is 8 bits wide (_ioc_typebits)
- Number
- Ordinal, 8-bit wide (_ioc_nrbits)
- Direction
- _ioc_none (no data transfer)
- _ioc_read
- _ioc_write
- _ioc_read | _ioc_write (bidirectional transmission of data)
- Size
- The size of the user data involved
- Typically 13-bit or 14-bit
- _ioc_sizebits
- <asm/ioctl.h>
- _io (Type, NR)
- Used to construct a command number without parameters
- _ior (Type, nr, datatype)
- The command number used to construct the data read from the driver
- _iow (Type, nr, datatype)
- Used to construct a command to write data
- _iowr (Type, nr, datatype)
- For bidirectional transmission
- _ioc_dir (NR)
- _ioc_type (NR);
- _IOC_NR (NR)
- _ioc_size (NR);
- return value
- The implementation of the IOCTL is usually a switch statement based on the command number
- Can't match any legitimate actions?
- Some kernel functions will return-einval
- The POSIX standard specifies that if an inappropriate IOCTL command parameter is used, the-enotty should be returned
- predefined commands
- predefined commands are divided into three groups
- commands available for any file (normal, device, FIFO, and socket)
- commands for normal files
- specific to file system types Command
- device driver developers are only interested in the first group, their magic numbers are "T"
- fioclex
- Fionclex
- clear off flag on execution
- fioasync
- set or reset file asynchronous notifications
- These two actions can all be done by fcntl, and no one actually uses Fioasync
- fioqsize
- Returns the size of a file or directory
- fionbio
- file ioctl non-blocking I/O
- Use IOCTL parameters
- <asm/uaccess.h>
- int access_ok (int type, const void *addr, unsigned long si Ze);
- type
- addr
- size
- Returns a Boolean value: 1 for success, 0 for failure
- If the return fails, the driver typically returns-efault to the caller
- Put_user (datum, PTR);
- __put_user (datum, PTR);
- get_user (local, PTR);
- __get_user (local, PTR);
- Empowerment and restricted operations
- Power-based (capability) system divides privileged operations into separate groups
- Capget
- Capset
- <linux/capability.h>
- Cap_dac_override
- The ability to limit access to files or directories
- Cap_net_admin
- Perform network management of any ability
- Cap_sys_module
- Ability to load or remove kernel modules
- Cap_sys_rawio
- Ability to perform "bare" I/O operations
- Cap_sys_admin
- Cap_sys_tty_config
- Ability to perform TTY configuration tasks
- <sys/sched.h>
- int capable (int capability);
- Blocking I/O
- A brief introduction to hibernation
- When a process is put into hibernation, it is marked as a special state and moved from the scheduler's run queue
- Never enter hibernation in an atomic context
- If the code sleeps while it has a semaphore, any other thread that waits for that Semaphore will also hibernate, so any code that has a semaphore to hibernate must be very short, and also ensure that the semaphore does not block the process that wakes us up.
- When we are awakened, we never know how long we have been dormant, or what has happened during hibernation.
- In Linux, a wait queue is managed through a "wait queue head"
- <linux/wait.h>
- wait_queue_head_t
- Declare_wait_queue_head (name);
- wait_queue_head_t My_queue;
- Init_waitqueue_head (&my_queue);
- Simple hibernation
- Wait_event (queue, condition);
- Wait_event_interruptible (queue, condition);
- Wait_event_timeout (queue, condition, timeout);
- Wait_event_interruptible_timeout (queue, condition, timeout);
- void Wake_up (wait_queue_head_t *queue);
- void Wake_up_interruptible (wait_queue_head_t *queue);
- Blocking and non-blocking operations
- An explicit non-blocking I/O is determined by the O_NONBLOCK flag in Filp->f_flags
- <linux/fcntl.h>
- Automatically included in <linux/fs.h>
- In the case of a blocking operation, the following actions should be implemented to maintain consistency with the standard semantics
- If a process calls read but no data is readable, the process must block
- If a process calls write but the buffer does not have space, the process must be blocked and must hibernate on a different wait queue than the read process
- Advanced hibernation
- How the process sleeps
- The first step in putting a process into hibernation is usually to assign and initialize a wait_queue_t structure and then add it to the corresponding wait queue
- The second step is to set the status of the process to mark it as hibernate
- <linux/sched.h>
- Task_running
- Task_interruptible
- Task_uninterruputible
- void set_current_state (int new_state);
- Discarding the processor is the last step, but there is another thing to do before: we must first check the conditions of hibernation wait
- if (!condition) schedule ();
- Manual hibernation
- <linux/sched.h>
- Define_wait (my_wait);
- wait_queue_t my_wait;
- Init_wait (&my_wait);
- void Prepare_to_wait (wait_queue_head_t *queue, wait_queue_t *wait, int state);
- State is the new status of the process and should be task_interruptible or task_uninterruptible
- Schedule ();
- void Finish_wait (wait_queue_head_t *queue, wait_queue_t *wait);
- Exclusive wait
- When a process calls Wake_up on the wait queue, all processes waiting on that queue are set to a running state
- Only one of the awakened processes can get the expected resources, while the other awakened processes will only hibernate again
- An exclusive wait behavior is similar to the usual sleep, but has the following two important differences
- When the wq_flag_exclusive flag is set for the queue entry, it is added to the end of the wait queue
- When WAKE_UP is called on a wait queue, it stops waking other processes after waking the first process with the WQ_FLAG_EXCLUSIVE flag
- The use of exclusive waits in the driver is worth considering if the following two conditions are met
- There is a serious competition for a resource, and waking up a single process can consume the resource completely
- void Prepare_to_wait_exclusive (wait_queue_head_t *queue, wait_queue_t *wait, int state);
- Related details of wake-up
- <linux/wait.h>
- WAKE_UP (wait_queue_head_t *queue);
- Wakes all non-exclusive waiting processes on the queue, as well as a single exclusive wait
- Wake_up_interruptible (wait_queue_head_t *queue);
- Skipping non-disruptive hibernation processes
- WAKE_UP_NR (wait_queue_head_t *queue, int nr);
- Only the Nr exclusive wait process is awakened
- WAKE_UP_INTERRUPTIBLE_NR (wait_queue_head_t *queue, int nr);
- Only the Nr exclusive wait process is awakened
- Wake_up_all (wait_queue_head_t *queue);
- Wake_up_interruptible_all (wait_queue_head_t *queue);
- Wake_up_interruptible_sync (wait_queue_head_t *queue);
- Old history: sleep_on
- void sleep_on (wait_queue_head_t *queue);
- void interruptible_sleep_on (wait_queue_head_t *queue);
- Never use them.
- Poll and select
- Poll, select, and Epoll system calls
- The functions of poll, select, and Epoll are essentially the same: both allow the process to decide whether a non-blocking read or write can be made to one or more open files
- Select is introduced in BSD Unix
- Poll introduced by System V
- unsigned int (*poll) (struct file *filp, poll_table *wait);
- Poll_table structure for implementing poll, select, and Epool system calls in the kernel
- <linux/poll.h>
- void poll_wait (struct file *, wait_queue_head_t *, poll_table *);
- The second task performed by the
- poll method is to return a bitmask that describes which operation can be executed immediately
- <linux/poll.h>
- Pollin
- If the device can read without blocking, set the Bit
- pollrdnorm
- if the "usual" data is ready to read, set the bit
- a readable device to return (pollin| Pollrdnorm)
- pollrdband
- This bit indicates that out-of-band data can be read from the device
- POLLPR I
- can read high-priority data without blocking
- pollhup
- When a process that reads a device reaches the end of the file, the driver must set the POLLHUP bit
- pollerr
- pollout
- If the device can write without blocking, set the bit in the return value
- pollwrnorm
- The bit is the same as pollout, and sometimes it's the same number.
- a writable device will return (pollout| Pollwrnorm)
- pollwrband
- Similar to Pollrdband, this one indicates that data with non-0 priority can be written to the device
- Pollrdband and Pollwrband are only meaningful in the case of socket-related file descriptions, and device drivers typically do not use these two flags
(Chang).
- interaction with Read and write
- read data from device
- if the input buffer has data, even if the data is ready to be made less than the program requested, and the driver guarantees that the remaining data will arrive immediately, the read call should still be delayed with imperceptible Immediately returns
- if there is no data in the buffer, read must block waits by default until at least one byte arrives, and if the O_nonblock flag is set, read should return immediately, and the return value is-eagain. Poll must report that the device is not readable
- if the end of the file has been reached, read should immediately return 0, at which point poll should report Pollhup
- Write data to the device
- if the output buffer , write should return without delay, in which case the poll report device can write
- if the output buffer is full, write is blocked by default until there is room to release, and if the O_nonblock flag is set, write should return immediately. The return value is-eagain. Poll must report that the device is not writable
- never let the write call wait until the data transfer ends before returning
- flush pending output
- int (*fsync) (struct File *file, struct dentry *dentry, int datasync);
- If your application needs to ensure that data has been transferred to the device, you must fsync the method
- Datasync is used to differentiate between Fsync and Fdatasync system calls
- The underlying data structure
- Poll_table structure is a simple package that makes up the actual data structure, and contains the poll_table_entry structure of the memory page linked list
- Each poll_table_entry structure includes a pointer to the struct file type of the open device, a wait_queue_head_t pointer, and an associated wait queue entry
- If no driver can make nonblocking I/O when polling (poll), the poll caller goes into hibernation until one or more of the waiting queues on the queue wake it Up
- The interesting thing about poll implementations is that the driver's poll method passes a null pointer for the plol_table parameter when the callee is called.
- At the end of the poll call, the poll_table structure is reassigned, and all waiting queue entries previously added to the poll table are removed from the table and the wait queue
- Asynchronous notification
- In order to enable the file bag asynchronous notification mechanism, a user program must perform two steps
- First, they specify a process as the owner of the file, and the process ID number of the master process is saved in Filp->f_owner when the process uses the FCNTL system call to execute the f_setown command
- Then, the user program must be in the device Fasync flag, completed by Fcntl F_SETFL command
- example
- struct sigaction action;
- memset (&action, 0, sizeof (action));
- Action.sa_handler = Sighandler;
- action.sa_flags = 0;
- sigaction (SIGIO, &action, NULL);
- fcntl (Stdin_fileno, F_setown, Getpid ());
- oflags = Fcntl (Stdin_fileno, F_GETFL);
- fcntl (Stdin_fileno, F_SETFL, Oflags | Fasync);
- from the driver's perspective
- detailed procedure from the kernel point of view
- F_setown is called when you assign a value to Filp->f_owner, and do nothing
- perform F_SETFL When using Fasync, the driver's Fasync method is called, so long as the FASYNC flag in Filp->f_flags changes, the method is called to notify the driver of the change so that it responds correctly to
- when the data arrives. All processes registered as asynchronous notifications will be sent a Sigio signal
- <linux/fs.h>
- struct fasync_struct
- int Fasy Nc_helper (int fd, struct file *filp, int mode, struct fasync_struct **fa);
- void Kill_fasync (struct fasync_struct **fa, int sig, int band); The
- sig is typically Sigio
- band, usually poll_in, equivalent to pollin| Pollrdnorm
- Some devices also implement asynchronous notifications for device writable, in which case Kill_fasync must call in Poll_out mode
- The Fasync method must be called when the file is closed
- Positioning equipment
- Llseek implementation
- If the Llseek method is not defined by the device operation, the kernel defaults by modifying the Filp->f_pos to perform the positioning
- If the location operation corresponds to a physical operation of the device, you may need to provide your own Llseek method
- If locating the device is meaningless, you should call Nonseekable_open in the Open method to notify the kernel that the device does not support Llseek
- int Nonseekable_open (struct inode *inode, struct file *filp);
- You should also set the Llseek method in the file_operations structure to a special auxiliary function No_llseek
- Access control for device files
- Exclusive equipment
- The most blunt method of access control is to allow only one process to open the device at a time
- Restrict access by only one user at a time
- Requires two data items
- An open count
- UID of the device owner
- Current->uid
- Current->euid
- Block Open alternative to Ebusy
- This is usually the most logical way to return an error when the device is unreachable, but in some cases the process may need to wait for the device
- Copying devices on Open
- Another way to implement access control is to create a different private copy of the device when the process opens the device
"Linux Device Drivers" Sixth advanced character driver Operation--note