串口驱动程序的编写总结(一)
8250/16450/16550芯片都用同个8250驱动
1、对现有驱动进行拷贝,然后进行局部修改
2、不必过多深入系统内核驱动的调用过程,区分好哪些是需要修改的,哪些是内核驱动自带的
3、对于要修改的内容,参考别人成功的例子,看哪些需要修改的
4、必要时,可以先把原拷贝先不加载进驱动,把自己拷贝的驱动加载进去
5、谨记要实现的功能,按步骤实现
6、知道每个模块的作用与功能,哪些是涉及硬件,哪些是涉及系统的,一般来说,进行设备、驱动的注册时,一般不涉及驱动,只有应用层调用时才进行硬件的相关调用。
7、对串口驱动程序的改造时如果是采用外部模块加载的方式,即insmod方式,而不是内置于内核生成vmlinux,则不能使用console驱动,否则编译会出现
error: redefinition of '__inittest' /opt/kangear/hello/hello.c:16: note: previous definition of '__inittest' was here
错误,会出现重定义的情况。
解决方法:去除console的相关驱动,屏蔽console_initcall()函数的调用
8、对串口的发送的配置属性,最终调用底层驱动的ioctl函数。而ioctl函数得执行copy_from_user、copy_to_user函数进行用户与内核之间的数据拷贝,而在ioctl函数执行这些操作后,底层的驱动程序才能继续对配置参数(波特率、数据位、停止位、检验位....)的设置
9、在用户层面操作open()函数时,会调用底层驱动的一系列默认配置参数,这是在uart_core.c文件里进行属性的配置
10、中断有分系统中断与外部中断, 系统中断在一开机时就已经初始好,而外部中断是在驱动程序启动时调用,而中断的触发是靠硬件进行中断请求,cpu响应进行处理
驱动详解:
1、在串口驱动中, 中断的产生都是用户态所触发引起的。在底层接收到中断后会进行数据往上推送或往底层数据硬件进行发送
具体可参看文章:http://blog.csdn.net/hui523hui523hui523/article/details/7521981
2、在强类型的C语言里,要严格区分双引号与单引号,双引号是一个指针,单引号代表的是一个值。
例如:
unsigned char a = "1"; unsigned char a='1';
在弱类型的语言里,双引号与单引号是混合用。例如php、asp、js。
3、usb总线协议规定的是底层硬件的通讯协议,在其上层还有其它各种各样的通讯协议,有低速、高速设备的协议 ,其中包括hid协议 。
hid协议是一种低速通讯的usb通讯,HID类设备属于人机交互操作的设备。 可用于键盘、鼠标等
4、底层数据是如何进行获取的往上推送的
通过用户层通过read时调用到底层的函数serial8250_handle_irq(),接着调用serial8250_rx_chars()进行数据接收及上层推送
5、C语言的.h与.c文件。.h文件主要是用来声明,一般不用来开辟内存空间。.c文件主要用来分配内存空间,初始化全局变量及静态变量。
由于设置为全局变量,在一个进程的内存空间是全局可见的。
一个.c文件要调用另一个.c文件的全局变量、静态变量时,需通过加入extern关键词。
例如:
a.c 文件
int m_gmsr = 1;
static m_socr = 2;
b.c文件
extern int m_gmsr;
m_gmsr = 2;
一个.c文件要调用另一个.c文件的函数时,只需要包含其该.c文件的头文件即可
6、C语言将声明与定义区分开,即将.c文件与.h文件区分开的好处是:
当在.c文件的一个函数另外该文件的另一个函数时,可解决其依赖关系,而不会出错。
例如:
a.c文件
int diff(int a,int b)
{
return a>b?a:b;
}
int max(int a,int b)
{
return diff(a,b);
}
如果没有定义头文件,diff函数要放在max函数前面,否则会出现undefined symbol diff函数的情况。在头文件声明这两个函数,则不会出现因位置而编译出错,具体是什么原因呢?
具体参看链接:http://www.cnblogs.com/webcyz/archive/2012/09/16/2688035.html
7、在C语言中,多使用类似这样
#ifndef XXX
#define XXX
#endif
8、多个文件编译成内核模块
参看链接:http://blog.csdn.net/alex_xhl/article/details/5719230
类似:
obj-m +=sahuLB.o sahuLB-objs:=simpLB.o sahu_lb_tools.o all: make -C /lib/modules/`uname -r`/build M=`pwd` clean: make -C /lib/modules/`uname -r`/build M=`pwd` clean install: /sbin/insmod sahuLB.ko remove: /sbin/rmmod sahuLB
标记红色部分要相同,而且生成的模块名不能与任何源文件的相同
9、有些硬件芯片不支持多次复位,就如sja1000芯片。如果要写驱动调用sja1000芯片,则不能用原先的sja1000芯片的驱动程序。
10、挂载在platform总线的硬件,可通过linux提供的函数的request_irq()进行中断的请求,相当于注册了一个回调函数,硬件寄存器通过中断来通知内核。中断是一种电信号,由硬件设备生成,并送入中断控制器 的输入引脚中,中断控制器会给CPU发送一个电信号,CPU检测到这个信号,就中断当 前的工作转而处理中断。每个中断都通过一个唯一的数字标志
11、打印字符数组时,char a[]="123"; 不能用printf("%s\n",a);这种方式,因为会导致没遇到结束符"\0",而不断的打印数据
12、
模块编译的过程:
my_uart_can: Unknown symbol uart_console_device (err 0)
my_uart_can: Unknown symbol platform_device_put (err 0)
my_uart_can: Unknown symbol uart_parse_options (err 0)
my_uart_can: Unknown symbol serial8250_do_shutdown (err 0)
my_uart_can: Unknown symbol serial8250_handle_irq (err 0)
my_uart_can: Unknown symbol platform_driver_unregister (err 0)
my_uart_can: Unknown symbol serial8250_modem_status (err 0)
my_uart_can: Unknown symbol platform_device_unregister (err 0)
my_uart_can: Unknown symbol uart_set_options (err 0)
my_uart_can: Unknown symbol platform_device_add (err 0)
my_uart_can: Unknown symbol platform_device_alloc (err 0)
my_uart_can: Unknown symbol platform_device_del (err 0)
my_uart_can: Unknown symbol __platform_driver_register (err 0)
my_uart_can: Unknown symbol serial8250_tx_chars (err 0)
my_uart_can: Unknown symbol nr_irqs (err 0)
my_uart_can: Unknown symbol uart_console_write (err 0)
解决方法:
加入MODULE_LICENSE("Dual BSD/GPL");以支持模块引用GPL的符号
13、
错误:
/home/nfs/drivers/can_uart/8250_core.c:3758: error: redefinition of '__inittest'
/home/nfs/drivers/can_uart/8250_core.c:3298: error: previous definition of '__inittest' was here
/home/nfs/drivers/can_uart/8250_core.c:3758: error: redefinition of 'init_module'
/home/nfs/drivers/can_uart/8250_core.c:3298: error: previous definition of 'init_module' was here
scripts/Makefile.build:263: recipe for target '/home/nfs/drivers/can_uart/8250_core.o' failed
解决方法及原因:
是console_initcall所引起的,
__inittest是由console_initcall所引起的
在串口作为模块进行加载时,是不能两个驱动存在,console_initcall() 及 module_init()不能同时存在,不能同时存在这两个入口函数。
而将串口驱动程序编译进内核,则可以存在多个入口函数。 console()控制台驱动是为了实现将串口当作控制台实现。
14、串口的波特率、停止位、数据位、检验位理解
波特率:即单位时间内振荡的次数,也即单位时间内发送多少bit的数据
停止位:主要是为了区分上次所发送的数据完毕所加上的位
数据位:对用户发送的一个字节,从低位开始取多少位,比如: 十进制值:31, bit位:00011111。 当数据位设为5时,则bit位变为00001111。用户接收到的十进制值:15。