linux-driven learning (1) - character device driver development

 (A) Introduction Drivers

(A) Linux driver to learn

knowledge structure:

1. Linux driver design pattern (40%)

2. kernel-related knowledge (30%)

3. hardware-related knowledge (30%)

(B) drive Category:

① character devices:

  Character device is a device to access the byte, character-driven character device driver is responsible for, such drives typically implement open, close, read, and write system calls.

② block device:

  In most Unix systems, a block device can not process the data byte, transmitting only once or a plurality of length (or a greater number of power of 2) 512-byte block of data, and allows Linux device block any number of bytes transferred. Thus, the difference between the block and character device driver only with a different interface to the kernel.

③ network interface:

  Any network transaction is carried out via an interface, an interface is usually a hardware device (eth0), but it can also be a pure software device, such as loopback interface (lo). A network interface responsible for sending and receiving data packets.

(C) using the driver:

A: Linux device user program files: the driver operation of the character to use and block devices (also known as node device)
Q: equipment (characters, blocks) in the file where?

(B) the character device driver

(A) Equipment Number:

① major and minor device numbers:

  Character device access via the character device file. Character device file identified by using the output of ls -l "c" of the first column. If you use the ls -l command, you will see two numbers (separated by a comma) major and minor device numbers These numbers are device files in the device file entry.

② role device number:

  Driver major number for identifying the device connected to the file. Drivers minor number is used to identify which device is operated.

    • Master device number to reflect the type of equipment
    • Minor number used to distinguish different types of devices

③ major and minor number:

Q: How do kernel described device number?
A: The essence of the dev_t ** unsigned int 32-bit integer, wherein the high resolution master device 12, 20 for the lower sub-device number.

Q: how to break out from dev_t major number?
A: MAJOR (dev_t dev)

Q: how to break out of the minor number from dev_t in?
A: MINOR (dev_t dev)

④ major number assigned:

Static application:

method:

The Documentation / devices.txt, a major number is determined not used

Use register_chrdev_region function device registration number

Pros: Simple

Cons: Once the drive is widely used, the random number selected master device may cause the device number conflicts, the driver can not register 

int register_chrdev_region(dev_t from, unsigned count, const char *name) 

Function: Application for use from the start of the count from a device number (major number unchanged, times increase device number)

parameter:

    • from: I would like to apply the device number to use
    • count: the number of applications want to use the device number
    • name: device name (reflected in the / proc / devices)

Dynamic allocation:

Methods: device number assigned alloc_chrdev_region

Pros: Simple, easy to drive promotion

Cons: Can not create file before installing the device driver (because the previous installation has not been assigned to a major number).

Solution: After installing the driver, query the device number from / proc / devices in

int alloc_chrdev_region (dev_t * dev, unsigned
baseminor, unsigned count, const char * name) Function: the kernel requests dynamic allocation to count device number, device number and the secondary start from baseminor.
parameter:

        • dev: device number assigned to
        • baseminor: starting minor device number
        • The number of device number to be assigned: count
        • name: device name (reflected in the / proc / devices)

⑤ cancellation device number:

void unregister_chrdev_region (dev_t from, unsigned count
) function: from the beginning of the count released from a device number

(B) create a device file

① created using the mknod command to manually
  mknod usage: mknod filename of the type Major Minor

    • filename: device file name
    • type: file type device
    • major: major number
    • minor: minor number

  Example: 100 0 C the mknod Serial0

② created automatically

(C) important structures:

In the Linux character device driver design, there are three very important data structure:

Struct File:

  It represents an open file. Each open file system in kernel space has an associated struct file. It is created by the kernel when you open the file, released after the close.
Important members:

    • loff_t f_pos / write location of a file * * /
    • struct file_operations *f_op

Struct Inode:

   For recording information on a physical file. Therefore, it represents the open files and file structure is different. A file can correspond to multiple file structures, but only one inode structure.
Important members:

    • dev_t i_rdev: device number

Struct file_operations:

  A set of function pointers can be defined on the device operation. The structure of the drive member to functions, these functions implement a special operation reserved for unsupported operation is NULL.

1 struct file_operations mem_fops = {
2 .owner = THIS_MODULE,
3 .llseek = mem_seek,
4 .read = mem_read,
5 .write = mem_write,
6 .ioctl = mem_ioctl, 7 .open = mem_open, 8 .release = mem_release, 9 }

 

(D) Device Registration

In linux 2.6 kernel, using struct cdev character device described. Character registered device can be divided into the following three steps:

    • Distribution cdev
    • Initialization cdev
    • Add cdev

① device registration (assignment)

Struct cdev distribution function may be accomplished using cdev_alloc.
struct cdev * cdev_alloc (void)

② device registration (initialization)

Struct cdev use cdev_init initialization function to complete.
void cdev_init (struct cdev * cdev,
const struct file_operations * fops) parameters:

    • cdev: structure to be initialized cdev
    • fops: set of operating devices corresponding to the function

③ device registration (add)

register struct cdev use cdev_add function to complete.
int cdev_add (struct cdev * p,
dev_t dev, unsigned count) Parameters:

    • p: the character to be added to the core device configuration
    • dev: device number
    • count: The number of devices added

④ equipment operation

int (* open) (struct inode
*, struct file *) The first operation on a file device, the driver must not required to implement this method. If this is NULL, opening the device always successful.

void (* release) (struct inode
*, struct file *) calls this operation when the device file is closed. And similar open, release may not

ssize_t (* read) (struct file
*, char __user *, size_t, loff_t *) to read data from the device.

ssize_t (* write) (struct file
*, const char __user *, size_t, loff_t *) data to the transmitting apparatus.

unsigned int (* poll) (struct
file *, struct poll_table_struct *) corresponding to the select system call

int (* ioctl) (struct inode
*, struct file *, unsigned int, unsigned long) control device

int (* mmap) (struct file * , struct vm_area_struct *)
the device mapped into the process virtual address space.

off_t (* llseek) (struct file
*, loff_t, int) to modify the current file position to read and write, and the new location as the return value.

Open method:
  Open the driver is used to complete the preparation for the subsequent initialization operations. In most drivers, open to complete the following tasks:

      • Initialize the device.
      • Indicate the minor number.

Release method:
  the role of the Release method of reverse of open. This device method is sometimes referred to close,
  it should:

      • Turn off your device

Reading and writing:
  read and write methods do similar things: reading data from the device to the user space; to pass data to the driver. Their prototype is quite similar:

      • ssize_t xxx_read(struct file * filp, char __user * buff, size_t count, loff_t *offp);
      • ssize_t xxx_write(struct file *filp, char __user * buff, size_t count, loff_t*offp);

For two methods, filp file pointer, count the amount of data transmitted request. buff parameter points to a data buffer. Finally, offp pointed out that the current location of file access.


Read and Write Parameter buff method is that the user space pointer. Therefore, it can not be a direct reference to the kernel code, for the following reasons:

User-space pointer in the kernel space may simply be ineffective --- that is not mapped address

The kernel provides the dedicated function pointer for accessing user space, for example:

    • int copy_from_user(void *to, const void __user *from, int n)
    • int copy_to_user(void __user *to, const void *from, int n)

⑤ equipment write-off

Logout character device using cdev_del function to complete.
int cdev_del (struct cdev * p)
parameters:

  • p: To log out of character device structure

(C) character driven case study


(D) drive commissioning technology

Debugging Techniques Category: For driver design, one of the core issue is how to accomplish debugging. Drive current common debugging techniques can be divided into:

    1. Print debugging
    2. Debugger to debug
    3. Query Debugging

(E) concurrency control

(A) concurrently with the race

Concurrently: a plurality of execution units execute simultaneously.
Competition state concurrent execution units access to shared resources (hardware resources on the global variables and software, etc.) due to: race
Example:

1 if (copy_from_user(&(dev->data[pos]), buf, count))
2 ret = -EFAULT;
3 goto out;

Suppose there are two processes are trying to write to the same location of a device, it will cause confusion data


Concurrent processing technology is used for locking or mutually exclusive, i.e., to ensure that only one execution unit may operate the shared resource at any time. In the Linux kernel primarily through semaphore mechanism and spin_lock mechanism.


① semaphore

    Semaphore Linux kernel on the concepts and principles and semaphores user mode is the same, but it can not be used outside of the kernel, which is a sleep lock. If there is a task you want to get the amount of signal has been occupied, the semaphore the process will be put in a waiting queue, and then allowed to sleep. When the process holds the semaphore signals release, the task is waiting in the queue will be awakened and allowed to obtain the semaphore

    Semaphore need to set an initial value at the time of creation, representation allows several tasks simultaneously accessing the semaphore to protect shared resources, the initial value of 1 becomes mutex (Mutex), that is, at the same time only one task can access sharing resources protection signal.
    When the task is complete access to shared resources protection signal, must release the semaphore, the semaphore release the semaphore value is achieved by adding 1, if the release of the semaphore is not positive, indicating the current task waiting for the semaphore , so to wake task waiting for the semaphore.
    Implement a semaphore is associated with the architecture defined in <asm / semaphore.h> in, struct semaphore used to indicate the type of semaphore


1. Definition semaphore

      • struct semaphore sem;

2. Initialize the semaphore

      • void sema_init (struct semaphore *sem, int val)
      • This function is used to set the initial value of the semaphore initialization number, it sets the value of the semaphore sem val.
      • void init_MUTEX (struct semaphore *sem)
      • This function is used to initialize a mutex, i.e. the value which is a semaphore sem.
      • void init_MUTEX_LOCKED (struct semaphore *sem)
      • This function is also used to initialize a mutex, but the value of the semaphore sem is 0, i.e., a locked state at the outset

Working definition and initialization macros may be one step below:

      • DECLARE_MUTEX (name)  defines a semaphore name, and initializes its value 1.
      • DECLARE_MUTEX_LOCKED (name) defines a semaphore name, but to set its initial value is 0, i.e. the lock is in the locked state at the time of creation.

3. Obtain Semaphore

void down(struct semaphore * sem)

Obtain Semaphore sem, it may cause the process to sleep, and therefore can not use this function in interrupt context. The value of this function will sem minus 1, if the value of the semaphore sem non-negative, direct return, otherwise the call will be suspended until the other task releases the semaphore can continue.

int down_interruptible(struct semaphore * sem)

Acquire Semaphore sem. If the semaphore is unavailable, the process will be set to the sleep state TASK_INTERRUPTIBLE type. This function returns a return value to distinguish between a normal signal or the interrupt return, if the return 0, indicating a normal return the semaphore is obtained, if the signal is interrupted, return -EINTR.

down_killable(struct semaphore *sem)

Acquire Semaphore sem. If the semaphore is not available, the process will be set to TASK_KILLABLE type of sleep state

  Note:
    Down () function is now not recommended for continued use. Recommended down_killable () or down_interruptible () function.


4. Release Semaphore

void up(struct semaphore * sem)

This function releases the semaphore sem, i.e. the value plus sem of 1, if the value is not positive sem, indicates a task waiting for the semaphore, and therefore wake up the waiter.


② spin locks

    Spin lock can only be a maximum of executable unit holders. Spin lock does not cause the caller to sleep, if one thread of execution trying to get a spin lock has been held, the thread will always be busy cycle, wait forever in there to see if the spin lock holder has released the lock, "spin" what it means.

    • spin_lock_init (x)  This macro is used to initialize spinlock x, spin locks must be initialized before use.
    • spin_lock (lock) acquire spin lock lock, if successful, immediately acquires the lock and returns immediately, otherwise it will always spin in there until the spin lock holder release.
    • spin_trylock (lock) attempts to acquire a spin lock lock, if we can get the lock immediately and return true, otherwise returns false immediately. It would not have been waiting to be released
    • spin_unlock (lock)  releases the spin lock lock, it is paired with spin_trylock or spin_lock.

③ PK semaphore spinlock

    Semaphore may allow multiple owners, while the spin lock at any time can only allow a holder. Of course, there mutex semaphore name (only a holder), a plurality of holders allows the semaphore is called counting semaphores.
    Semaphore suitable for holding time is long; the spinlock is adapted to keeping time is very short, the code in the practical application of the spin-lock control only a few lines and the time spinlock is held also generally not more than twice a context switch time, because once the thread to be switched on at least two cut spending cut, spin lock occupancy time is much longer than if two context switches, we should choose the semaphore

 

 

 

 

 

 

Guess you like

Origin www.cnblogs.com/WenLee/p/12114416.html