0. Preface
Graduate life all are on the right track, I also started a new study, because really do not want to make storage, so decided to study with the elder brother device driver, read the book two weeks, finally a little clue, began to record it!
1. Preparatory work
A) View the kernel version
Uname-r
b) Install kernel source tree (http://www.cnblogs.com/Jezze/archive/2011/12/23/2299871.html)
Download the source code in www.linux.org, here is the. xz format, need to install the decompression tool, xz-utils;
Decompression Method Example: Xz-d linux-3.1-rc4.tar.xz
TAR-XF Linux-3.1-rc4.tar
c) Installing Kernel Functions Man Handbook
compile make mandocs; install make installmandocs; Test man PRINTK.
2. A simple understanding of character drivers
A) I understand that the driver is the use of Linux kernel functions to write a kernel module, the implementation of the device files open, close, read and write, control and other operations, this will be the structure of the device file structures in-depth understanding, people are relieved that the basic framework of the driver is not changed, the difficulty is that the program involves concurrency control, memory allocation, interrupt and other problems, so it will be more complex, so it is a long way to go.
b) three important data structures : The file_operations structure is defined primarily by a variety of function pointers, by defining a series of functions of the device, and then assigning the function pointer, the device and function are completed. The driver implements the mapping of the operation of the system call to the actual hardware device, the file struct represents an open descriptor, which has an open flag (read only), a file pointer, and so on, which corresponds to an open device file, and the inode structure is primarily used to correspond to the file meaning on the hard disk. Each new file on the hard disk corresponds to an inode node, including inode number, block number, size and other information (do not understand when this, ruan a peak, understand inode:http://www.ruanyifeng.com/blog/2011/12/ inode.html), view their location in/usr/src/linux-3.**.pae/include/linux/fs.h.
c) analysis of the Linux kernel driver module , this article describes the module loading into the kernel of the specific process.
http://www.ibm.com/developerworks/cn/linux/l-lkm/
d) device files: The realization of the hardware device abstraction, so that the hardware read and write equivalent to the device file read and write.
e) main device number and secondary device number: The main device number is used to denote different kinds of devices, the secondary device number is mainly used to distinguish the device. (http://blog.csdn.net/gqb_driver/article/details/8805179).
3. Test the HelloWorld module
a) Source Code
#include <linux/init.h> #include <linux/module.h>module_license ("Dual BSD/GPL"), Static int Hello_init ( void) { printk (kern_alert "Hello, world\n"); return 0;} static void Hello_exit (void) { printk (kern_alert "Goodbye, cruel world\n");} Module_init (Hello_init); Module_exit (Hello_exit);
b) Makefile
Ifneq ($ (kernelrelease),) obj-m: = Hello.oelse kerneldir? =/lib/modules/$ (Shell uname-r)/buildpwd: = $ (shell PWD) Default: $ (make)-C $ (kerneldir) m=$ (PWD) modulesendif
c) load and unload, here to switch to command-line mode (CTRL alt F1 switch to command-line mode, CTRL alt F7 switch to graphical interface mode) will not show the results, otherwise need to be viewed in/VAR/LOG/SYSLG.
4. A simple character device driver
A) Program source code scull.h SCULL.C
#ifndef __scull_h__#define __scull_h__#include <linux/init.h> #include <linux/module.h> #include < linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/fcntl.h> #include <linux/cdev.h> #include <linux/ioctl.h> #include <asm/uaccess.h># Define Scull_major 0#define Scull_nr_devs 4#define scull_quantum 100#define scull_qset 10#define SCULL_IOC_MAGIC ' C ' # Define Scull_iocreset _io (scull_ioc_magic, 0) #define Scull_iocsquantum _iow (scull_ioc_magic, 1, int) #define Scull_ Iocsqset _iow (scull_ioc_magic, 2, int) #define Scull_ioctquantum _io (scull_ioc_magic, 3) #define Scull_ioctqset _io (Scull_ioc_magic, 4) #define Scull_iocgquantum _ior (scull_ioc_magic, 5, int) #define SCULL_IOCGQSET _ior (SCULL_IOC _magic, 6, int) #define Scull_iocqquantum _io (scull_ioc_magic, 7) #define Scull_iocqqset _io (scull_ioc_magic, 8) #defi Ne scull_iocxquantum _iowr (scull_ioc_magic, 9, int) #define Scull_iocxqset _iowr (scull_ioc_magic, ten, int) #define Scull_iochquantum _io (scull_ioc_magic, one) #define Scull_ioch Qset _io (scull_ioc_magic, N) #define SCULL_IOC_MAXNR 12struct scull_qset{void **data; Quantum set array struct scull_qset *next;}; struct scull_dev{struct scull_qset *data; Quantum set linked list pointer int quantum; Quantum set size int qset; The size of the quantum set array unsigned long size; Data size of struct semaphore sem; struct Cdev cdev;}; int scull_init_module (void); void scull_cleanup_module (void); int Scull_open (struct inode *, struct file *); int Scull_release (struct inode *, struct file *); loff_t scull_llseek (struct file *, loff_t, int); Long Scull_ioctl (Struc T file *, unsigned int, unsigned long), ssize_t scull_read (struct file *, char __user *, size_t, loff_t*); ssize_t Scull_wri Te (struct file *, const char __user*, size_t, loff_t *); void Scull_setup_cdev (struct scull_dev *, int); int scull_tr Im (struct scull_dEV *); struct scull_qset *scull_follow (struct scull_dev *, int); #endif # include "scull.h" int scull_major = Scull_major;int Scull_minor = 0;int Scull_nr_devs = scull_nr_devs;int Scull_quantum = scull_quantum;int Scull_qset = SCULL_QSET;struct SC Ull_dev *scull_devices;struct file_operations scull_fops = {. Owner = This_module,. Llseek = Scull_llseek,. Re AD = Scull_read,. Write = Scull_write,. Unlocked_ioctl = Scull_ioctl,. Open = Scull_open,. Release = Scull_release,};int scull_open (struct inode *inode, struct file *filp) {struct Scull_dev *dev; dev = container_of (inode->i_cdev,struct scull_dev, Cdev); Filp->private_data = Dev; PRINTK (kern_warning "in open\n"); if ((Filp->f_flags & o_accmode) = = o_wronly) {if (down_interruptible (&dev->sem)) Return-ere Startsys; Scull_trim (Dev); Up (&dev->sem); } return 0;} int scull_release (struct inode *inode, struct file *filp) {return 0;} Loff_t scull_llseek (struct file *filp, loff_t off, int whence) {struct Scull_dev *dev = filp->private_data; loff_t newpos = 0; Switch (whence) {case 0://seek_set newpos = off; Break Case 1://seek_cur newpos + = off; Break Case 2://seek_end Newpos + = dev->size + off; Break Default:return-einval; } if (Newpos < 0) Return-einval; Filp->f_pos = Newpos; return newpos;} ssize_t scull_read (struct file* filp, char __user *buf, size_t count, loff_t *f_pos) {struct Scull_dev *dev = filp-> Private_data; struct Scull_qset *dptr; int quantum = dev->quantum; int qset = dev->qset; int itemsize = Quantum * qset; int item, S_pos, Q_pos, rest; ssize_t retval = 0; if (down_interruptible (&dev->sem)) Return-erestartsys; if (*f_pos >= dev->size) goto out; if (*f_pos + Count > Dev->size) Count = dev->size-*f_pos; Item = (long) *f_pos/itemsize; Rest = (long) *f_pos% ItemSize; S_pos = Rest/quantum; Q_pos = rest% Quantum; Dptr = Scull_follow (dev, item); if (dptr = = NULL | |!dptr->data | |!dptr->data[s_pos]) goto out; if (Count > Quantum-q_pos) count = Quantum-q_pos; if (Copy_to_user (buf, Dptr->data[s_pos] + Q_pos, count)) {retval =-efault; Goto out; } *f_pos + = count; retval = Count;out:up (&dev->sem); return retval;} ssize_t scull_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {struct Scull_dev *dev = fi lp->private_data; struct Scull_qset *dptr; int quantum = dev->quantum; int qset = dev->qset; int itemsize = Quantum * qset; int item, rest, S_pos, Q_pos; ssize_t retval =-enomem; PRINTK (kern_info "before down_interruptible!\n");if (down_interruptible (&dev->sem)) Return-erestartsys; Item = (long) *f_pos/itemsize; Rest = (long) *f_pos% ItemSize; S_pos = Rest/quantum; Q_pos = rest% Quantum; Dptr = Scull_follow (dev, item); if (dptr = = NULL) goto out; PRINTK (kern_info "before kmalloc!\n"); if (!dptr->data) {dptr->data = Kmalloc (Qset * sizeof (char *), gfp_kernel); if (!dptr->data) goto out; memset (dptr->data, 0, Qset * sizeof (char *)); } if (!dptr->data[s_pos]) {Dptr->data[s_pos] = Kmalloc (Quantum, Gfp_kernel); if (!dptr->data[s_pos]) goto out; } if (Count > Quantum-q_pos) count = Quantum-q_pos; if (Copy_from_user (Dptr->data[s_pos] + q_pos, buf, Count)) {retval =-efault; Goto out; } *f_pos + = count; retval = count; if (Dev->size < *f_pos) Dev->size = *f_pos;out:up (&dev->sem); return retval;} Long Scull_ioctl (strUCT file *filp, unsigned int cmd, unsigned long arg) {int err = 0, tmp; int retval = 0; if (_ioc_type (cmd)! = scull_ioc_magic) Return-enotty; if (_ioc_nr (cmd) > Scull_ioc_maxnr) return-enotty; if (_ioc_dir (cmd) & _ioc_read) Err =! ACCESS_OK (Verify_write, (void __user *) arg, _ioc_size (cmd)); else if (_ioc_dir (cmd) & _ioc_write) Err =! ACCESS_OK (Verify_read, (void __user *) arg, _ioc_size (cmd)); if (err) Return-efault; Switch (CMD) {case scull_iocreset:scull_quantum = scull_quantum; Scull_qset = Scull_qset; Break Case Scull_iocsquantum:if (!capable (cap_sys_admin)) return-eperm; retval = __get_user (scull_quantum, (int __user*) arg); Arg is a pointer break; Case Scull_ioctquantum:if (!capable (cap_sys_admin))//ARG is a numeric value Return-eperm; Scull_quantum = ARg Break Case scull_iocgquantum:retval = __put_user (scull_quantum, (int __user*) arg); Break Case Scull_iocqquantum:return Scull_quantum; Case Scull_iocxquantum:if (!capable (cap_sys_admin)) return-eperm; TMP = Scull_quantum; retval = __get_user (scull_quantum, (int __user*) arg); if (retval = = 0) retval = __put_user (tmp, (int __user*) arg); Break Case Scull_iochquantum:if (!capable (cap_sys_admin)) return eperm; TMP = Scull_quantum; Scull_quantum = arg; return arg; Case Scull_iocsqset:if (!capable (cap_sys_admin)) return-eperm; retval = __get_user (Scull_qset, (int __user*) arg); Break Case Scull_ioctqset:if (!capable (cap_sys_admin)) return-eperm; Scull_qset = arg; Break Case scull_iocgqset:retval = __put_user (Scull_qset, (int __user*) arg); Break Case Scull_iocqqset:return Scull_qset; Case Scull_iocxqset:if (!capable (cap_sys_admin)) return-eperm; TMP = Scull_qset; retval = __get_user (Scull_qset, (int __user*) arg); if (retval = = 0) retval = __put_user (tmp, (int __user*) arg); Break Case Scull_iochqset:if (!capable (cap_sys_admin)) return-eperm; TMP = Scull_qset; Scull_qset = arg; return TMP; Default:return-enotty; } return retval;} struct Scull_qset *scull_follow (struct scull_dev *dev, int n) {struct Scull_qset *qs = dev->data; if (!qs) {qs = Dev->data = kmalloc (sizeof (struct scull_qset), gfp_kernel); if (QS = = null) return null; memset (QS, 0, sizeof (struct scull_qset));} while (n--) {if (!qs->next) {qs->next = Kmalloc (sizeof (struct scull_qset), gfp_kernel); if (Qs->next = = NULL) memset (qs->next, 0, sizeof (struct scull_qset)); } QS = qs->next; Continue } return QS; void Scull_setup_cdev (struct scull_dev *dev, int index) {int err, Devno = MKDEV (scull_major, Scull_minor + index); Device number//1. Initialize character device Cdev_init (&dev->cdev, &scull_fops); Dev->cdev.owner = This_module; 2. Add the character device to the kernel err = Cdev_add (&dev->cdev, Devno, 1); if (err) PRINTK (kern_notice "Errno%d adding scull%d", err, index);} int Scull_trim (struct Scull_dev *dev) {//Empties Scull data field struct Scull_qset *next, *dptr; int q_set = dev->qset; int i; for (dptr = dev->data; dptr, dptr = next) {if (Dptr->data) {for (i = 0; i < Q_set; i++) Kfree (Dptr->data[i]); Kfree (Dptr->data); Dptr->data = NULL; } next = dptr->next; Kfree (DPTR); } dev->size = 0; Dev->quantum = Scull_quantum; Dev->qset = Scull_qset; Dev->data = NULL; return 0;} void Scull_cleanup_module (void) {int i; dev_t Devno = MKDEV (Scull_major, Scull_minor); 1. Free memory Space if (scull_devices) {for (i = 0; i < Scull_nr_devs; i++) {Scull_trim (scull_devices + i); Cdev_del (&scull_devices[i].cdev); } kfree (scull_devices); } PRINTK (kern_warning "Rmmod module!"); 2. Release the device number Unregister_chrdev_region (Devno, Scull_nr_devs);} int Scull_init_module (void) {int result, I; return value index dev_t dev = 0; Device number//1. Request Device number if (scull_major) {//known master device number dev = MKDEV (scull_major, Scull_minor); result = Register_chrdev_region (Dev, Scull_nr_devs, "scull"); } else{result = Alloc_chrdev_region (&dev, Scull_minor, Scull_nr_devs, "scull");//I don't knowRoad Master device Number Scull_major = major (Dev); The kernel dynamically assigns the appropriate} if (Result < 0) {PRINTK (kern_warning "Scull:can ' t get major%d\n", scull_major); return result; }//2. Request memory space for the device scull_devices = Kmalloc (Scull_nr_devs * sizeof (struct scull_dev), gfp_kernel); if (!scull_devices) {result =-enomem; Goto fail; } memset (scull_devices, 0, Scull_nr_devs * sizeof (struct scull_dev)); 3. Initialize each device for (i = 0; i < Scull_nr_devs; i++) {scull_devices[i].quantum = Scull_quantum; Scull_devices[i].qset = Scull_qset; Sema_init (&scull_devices[i].sem, 1); Scull_setup_cdev (&scull_devices[i], i); Initialize the character device structure body} return 0; Fail:scull_cleanup_module (); return result;} Module_param (scull_major, int, s_irugo); Module_param (Scull_minor, int, s_irugo); Module_param (Scull_nr_devs, int, S_ Irugo); Module_param (scull_quantum, int, s_irugo); Module_param (Scull_qset, inT, S_irugo); Module_author ("Monica Lee"); Module_license ("Dual BSD/GPL"); Module_init (Scull_init_module); Module_exit (Scull_cleanup_module);
c) test file
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h>int main (int argc, const char *argv[]) { char buffer[20] = "Hello world!"; int FD, count; Write data to the character device fd = open ("/dev/scull0", o_wronly); if (fd = =-1) perror ("Open failed"); Count = Write (fd, buffer, strlen (buffer)); if (count = =-1) perror ("Write failed"); else printf ("Write count:%d\n", count); Close (FD); Read data from the character device memset (buffer, 0, sizeof buffer); FD = open ("/dev/scull0", o_rdonly); if (fd = =-1) perror ("Open failed"); Count = Read (fd, buffer, sizeof buffer); if (count = =-1) perror ("Read failed"); printf ("Read Data:%s\n", buffer); Close (FD); return 0;}
d) Compile and execute process (sudo mode)
Make
Insmod loading into the kernel;
Cat/proc/devices View the main device number;
Mknod/dev/scull0 C 250 0 Create a device file;
Execute test procedure;
Rmmod unload the module.
e) errors encountered during commissioning Error:
Neither the IOCTL function nor the Init_mutex function exist in the latest version of the kernel, the original int (*ioctl) (struct inode*, struct file*, unsigned int, unsigned long), and was changed to a long ( *UNLOCKED_IOCTL) (struct file *, unsigned int, unsigned long), Init_mutex function, using Sema_init (SEM, 1) instead.
0915-----Linux Device-driven learning note----------A simple character device driver