­

01Linux驱动模块01

一、驱动基础

1、什么是驱动

能够通过软件操作硬件的这份程序就是驱动

2、Linux驱动和ARM裸机驱动的区别

  1)Linux设备驱动工作时依赖于Linux内核, ARM裸机驱动不依赖于Linux内核,可以单独执行。 
  2)Linux设备驱动工作的空间在内核空间的【3G~4G】中,驱动中不能有main函数出现,也不能有while(1)的死循环, ARM裸机驱动的代码都在一个main函数中编写完成的。 
  3)Linux设备驱动可以并行执行(核与核之间可以并行执行,单个核时也可以分时复用执行) ARM裸机驱动的程序是在一个while中分时复用执行的 

3、驱动在内核中的位置

------------------------------------------------------------------------------
user(应用层):
	APP         APP    glibc(C标准库)
	         -----------------------------------------------------
		    shell:
-----------------系统调用(swi软中断)-------------------------------------------
kernel(内核层):(5大功能)
	1)文件管理
	2)内存管理
	3)进程管理
	4)网络管理:网络协议栈管理(网络的几个层次,TCP/IP四层、OSI七层)
	5)设备管理:设备驱动管理
-----------------------------------------------------------------------------
hardware(硬件层):
	LED、鼠标、键盘、LCD(帧缓存)、camera、声卡
	emmc、U盘、硬盘、dm9000、TRL8211
-----------------------------------------------------------------------------------------------

4、驱动的种类

1)字符设备驱动:

按照字节流来访问,并且只能顺序访问,不能无序访问的设备。

2)块设备驱动:

按照block(块 = 512字节)来访问,可以顺序访问,也可以无序访问的设备。

3)网卡设备驱动:

和网络通讯相关的设备驱动,没有设备文件,只能通过socket接口来访问的设备驱动

5、Linux内核模块

Linux内核模块的三要素:

1)入口: 驱动资源的申请,驱动对象的注册
2)出口: 驱动资源的释放,驱动资源的注销
3)许可证: 遵循 GPL 协议

GNU:GNU IS NOT UNIX 一个提倡软件自由的组织—>理查德·斯托曼提出的
GPL:GNU通用公共许可证 一种协议

C语言为面向过程的语言,C++为面向对象的语言
驱动使用的是C语言编程,但要有面向对象的思想和概念

模块的编写:!!!

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 
 4 //入口
 5 static int __init mycdev_init(void)
 6 {
 7     //__init作用:告诉编译器将mycdev_init这个函数放到.init.text这个段中
 8     return 0;
 9 }
10 
11 //出口
12 static void __exit mycdev_exit(void)
13 {
14     //__exit作用:告诉编译器将mycdev_exit这个函数放到.exit.text这个段中
16 }
17 
18 module_init(mycdev_init);
19 //告诉内核入口函数的地址是mycdev_init
20 module_exit(mycdev_exit);
21 //告诉内核出口函数的地址是mycdev_exit
22 MODULE_LICENSE("GPL");
23 //许可证
24

系统关键字

1、vi -t __ini
   vim ./include/linux/init.h +44
44 #define __init      __section(.init.text) __cold notr                 驱动模块入口段

2、vi -t __exit
   vim ./include/linux/init.h +83
83 #define __exit     __section(.exit.text) __exitused __cold notrace    驱动模块出口段


以"__"开头的内容,是给编译器使用的,不需要太过理解,
如 __init、__exit、__FILE____func____LINE__

static 关键字

在模块的入口函数和出口函数中使用了 static 关键字进行了修饰

  作用:是为了避免,自己自定义的模块入口出口函数名,与未知的函数名重名,而带来的重新定义的问题, 函数可以加上 static 关键字修饰,经过 static 关键字修饰过的函数的链接属性为内部,从而解决了该问题 

6、测试:

安装 insmod xxx.ko
查看 lsmod
卸载 rmmod xxx

7、Makefile编写

  KERNELDIR = /lib/modules/$(shell uname -r)/build/
  #ubuntu内核的路径
  
  #KERNELDIR = /home/liu/Linux/kernel-3.4.39/
  #开发板内核的路径

  PWD=$(shell pwd)
  #当前路径

  NAME ?= demo.c
  #需要编译的模块名字

  NAMES := $(patsubst %.c,%,$(NAME))
  #截取字符串 xx.c 里的 .c 之前的内容

  obj-m:=$(NAMES).o
  #需要的依赖文件

  all:
        	make -C $(KERNELDIR) M=$(PWD) modules

      @# mkae -C $(KERNELDIR)
      @# 进入内核顶层目录下,读取目录下的Makefile文件,并执行这个Makefile文件
      @# M=$(PWD) 	指定编译模块的路径
      @# modules 	编译模块的选项
      @# 如:
      @# 		1、cd $(KERNELDIR) 
      @# 		2、mkae M=$(PWD) modules
  clean:
      make -C $(KERNELDIR) M=$(PWD) clean

      @# mkae -C $(KERNELDIR)
      @# 进入内核顶层目录下,读取目录下的Makefile文件,并执行这个Makefile文件
      @# M=$(PWD)  指定编译模块的路径
      @# clean     清除的选项
      @# 如:
      @# 		1、cd $(KERNELDIR)
      @# 		2、make M=$(PWD) clean
  cp:
      cp $(NAMES).ko ~/nfs/rootfs/