- Main device number and secondary device number
- Those names are called special files, device files, or nodes that are simply called file system trees, and they are usually located in the/dev directory
- Typically, the main device number identifies the driver that corresponds to the device
- One main device number corresponds to one driver
- Internal representation of the device number
- dev_t (<linux/types.h>)
- Dev_t is a 32-bit number, 12 bits represents the main device number, the remaining 20 bits represent the secondary device number
- <linux/kdev_t.h>
- MAJOR (dev_t Dev);
- MINOR (dev_t Dev);
- MKDEV (int major, int minor);
- Assigning and releasing device numbers
- <linux/fs.h>
- int Register_chrdev_region (dev_t first, unsigned int count, char *name);
- int alloc_chrdev_region (dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
- void U nregister_chrdev_resion (dev_t first, unsigned int count);
- Dynamically assign a master device number
- Drivers should always use alloc_chrdev_region instead of register_chrdev_region functions
- The disadvantage is that the device node cannot be pre-created because the assigned master device number is not guaranteed to always be consistent
- /proc/devices
- Best way to assign a master device number
- The default is dynamic allocation, while retaining the scope to specify the main device number when loading or even compiling
- Some of the important data structures
- Three important kernel data structures
- File_operations
- File
- Inode
- File operations
- The file_operations structure is used to connect driver operations to the device number
- <linux/fs.h>
- File_operations structures or pointers to such structures are called fops
- Each field must point to a function in the driver that implements a specific action
- struct Module *owner
- loff_t (*llseek) (struct file *, loff_t, int);
- ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
- ssize_t (*aio_read) (struct KIOCB *, Char __user *, size_t, loff_t);
- ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
- ssize_t (*aio_write) (struct KIOCB *, const char __user *, size_t, loff_t *);
- Int (*readdir) (struct file *,, void *, filldir_t);
- unsigned int (*poll) (struct file *, struct poll_table_struct *);
- Int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
- Int (*mmap) (struct file *, struct vm_area_struct *);
- Int (*open) (struct inode *, struct file *);
- Int (*flush) (struct file *);
- Int (*release) (struct inode *, struct file *);
- Int (*fsync) (struct file *, struct dentry *, int);
- Int (*aio_fsync) (struct KIOCB *, int);
- Int (*fasync) (int, struct file *, int);
- Int (*lock) (struct file *, int, struct file_lock *);
- ssize_t (*READV) (struct file *, const struct IOVEC *, unsigned long, loff_t *);
- ssize_t (*writev) (struct file *, const struct IOVEC *, unsigned long, loff_t *);
- ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
- ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
- unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
- Int (*check_flags) (int)
- Int (*dir_notify) (struct file *, unsigned long);
- File structure
- The file structure represents an open document
- <linux/fs.h>
- There is no association with file in the user space program
- Pointers to struct file are often referred to as file or Filp
- Field
- mode_t F_mode
- loff_t F_pos
- unsigned int f_flags
- struct File_operations *f_op
- void *private_data
- struct Dentry *f_dentry
- Inode structure
- Kernel uses inode structure to represent files internally
- For a single file, there may be many file structures that represent open files descriptors
- Field
- dev_t I_rdev
- struct Cdev *i_cdev
- Registration of character devices
- <linux/cdev.h>
- get a separate Cdev structure
- struct Cdev *my_cdev = Cdev_alloc ();
- my_cdev->ops = &my_fops;
- owner
- struct Cdev *cdev_alloc (void);
- void Cdev_init (struct cdev *cdev, struct file_operations *fops);
- int Cdev_add (struct cdev *dev, dev_t num, unsigned int count);
- void Cdev_del (struct cdev *dev);
- Open and release
- Open method
- Main work
- Check for device-specific errors
- If the device is first opened, it is initialized
- Update the F_OP pointer if necessary
- Assign and fill in the data structure placed in Flip->private_data
- Int (*open) (struct inode *inode, struct file *filp);
- <linux/kernel.h>
- Container_of (pointer, Continer_type, Container_field);
- Release method
- Main work
- Frees all content that is assigned by open and saved in Filp->private_data
- Turn off the device during the last shutdown operation
- Scull Memory usage
- <linux/slab.h>
- void *kmalloc (size_t size, int flags);
- void Kfree (void *ptr);
- A non-Kmalloc returned pointer should not be passed to Kfree
- It is legal to pass a null pointer to Kfree
- Read and Write
- ssize_t Read (struct file *filp, char __user *buff, size_t count, loff_t *OFFP);
- ssize_t Write (struct file *filp, const char __user *buff, size_t count, loff_t *OFFP);
- Kernel code cannot directly reference user-space pointers
- The pointer to user space may be invalid when running in kernel mode, depending on the schema that the driver is running or the kernel configuration.
- Even though the pointer represents the same thing in kernel space, the memory of the user space is paged, and the memory involved may not be in RAM at all when the system call is called
- User-space pointers are provided by the user program, which may be defective or a malicious program
- The buffer that accesses the user space should always be done through the specialized functions provided by the kernel
- <asm/uaccess.h>
- unsigned long copy_to_user (void __user *to, const void * from, unsigned long count)
- unsigned long copy_from_user (void *to, const void __user *from, unsigned long count);
- Any function that accesses user space must be reentrant, must be able to execute concurrently with other driver functions, and must be in a state that can legally hibernate
- Read method
- If the return value is equal to the count parameter for passing the read system call, the number of bytes requested has been successfully completed
- If the return value is positive, but smaller than count, it indicates that the partial data transfer was successful
- If the return value is, it means that the end of the file has been reached
- A negative value means an error occurred, indicating what error occurred and the error code defined in <linux/error.h>
- There are no data yet, but there may be
- Write method
- Readv and Writev
- ssize_t (*READV) (struct file * filp, const struct IOVEC *iov, unsigned long count, loff_t *ppos);
- ssize_t (*writev) (struct file *filp, const struct IOVEC *iov, unsigned long count, loff_t *ppos);
- IOVEC structure
- void __user *iov_base;
- __kernel_size_t Iov_len
"Linux Device Drivers" chapter III character device driver--note