串⼝驱动程序的编写总结(⼀)
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、在串⼝驱动中,中断的产⽣都是⽤户态所触发引起的。在底层接收到中断后会进⾏数据往上推送或往底层数据硬件进⾏发送
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函数的情况。在头⽂件声明这两个函数,则不会出现因位置⽽编译出错,具体是什么原因
呢?
7、在C语⾔中,多使⽤类似这样
#ifndef XXX
#define XXX
#endif
8、多个⽂件编译成内核模块
类似:
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所引起的模拟串口使用printf函数
在串⼝作为模块进⾏加载时,是不能两个驱动存在,console_initcall() 及 module_init()不能同时存在,不能同时存在这两个⼊⼝函数。
⽽将串⼝驱动程序编译进内核,则可以存在多个⼊⼝函数。 console()控制台驱动是为了实现将串⼝当作控制台实现。
14、串⼝的波特率、停⽌位、数据位、检验位理解
波特率:即单位时间内振荡的次数,也即单位时间内发送多少bit的数据
停⽌位:主要是为了区分上次所发送的数据完毕所加上的位
数据位:对⽤户发送的⼀个字节,从低位开始取多少位,⽐如: ⼗进制值:31, bit位:00011111。当数据位设为5时,则bit位变为00001111。⽤户接收到的⼗进制值:15。