自己写的intel8250串口芯片驱动.docx
《自己写的intel8250串口芯片驱动.docx》由会员分享,可在线阅读,更多相关《自己写的intel8250串口芯片驱动.docx(23页珍藏版)》请在冰豆网上搜索。
自己写的intel8250串口芯片驱动
CU网友自己写的intel8250串口芯片驱动
一:
前言
串口是一种常用的接口,嵌入式开发环境中,开发板通常都会提供串口与PC相连,方便开发者进行测试。
在较早的网络环境中。
UNIX主机通过串口连moden再接通电话线来连通对方电脑。
类似于今天的telnet。
串口经常用来做远程终端使用。
类似于我们之前分析的终端控制台驱动。
不过,不相同的是,终端驱动的输入数据是从键盘鼠标等I/O外设中来,到显示器上显示。
而串口终端的数据来源跟数据输出都是串口。
对于运行中的进程来说,它不需要知道运行在什么样的终端。
Tty层把终端层给封装起来了.
查找了一相有关PC平台上的8250串口芯片资料,结合之前分析的uart架构自己写了一个串口驱动。
在写驱动的过程中,并没有参考linux自带的8250芯片驱动。
目的是为了在写完之后,和linux自带的驱动比较,就能发现自己的不足。
在驱动中,按着对端设备的数据模式设定了波特率和数据格式。
并末实现termios库中关于串口参数的设定。
不过在驱动中都写好了接口函数。
按操作接口将其链入即可。
另外:
忽略了moden信号的处理。
二:
串口的硬件架构
在pc中常使用的串口芯片是8250,16450,16450A等。
这些芯片都是从8250发展而来的。
都往下与8250保持兼容。
由于我手头只有8250和16450的详细的资料,代码分析时侧重于这两种类型的芯片分析.如果有朋友能够提供其它芯片的资料,我会感激不尽^_^.
8250提共了9个寄存器。
严格说来,只有8个。
因为其中有两个寄存器是共享同一个寄存器。
各寄存器的作用与寄存位的含义如下表所示:
在上图的端口地址标识中,小括号中还有一个地址。
这是因为在PC中。
一般都会有两个串口。
括号外的是主串口的端口地址,而括号里面的是从串口地址。
从上图可以看到8250的寄存器比较繁多,操作比较复杂。
我们依次来看每个寄存器的含义:
数据接收寄存器(RBR):
存放接收到的数据。
这些数据是已经去掉了发送位,停止位和奇偶检验位了的。
也就是串口接收到的有效数据。
数据发送寄存器(THR):
用来存放要发送的数据。
这是用户要写入的数据。
不包含上述的附加位。
从上图中可以看到,RBR和THR的端口地址是一样的。
也就是说,它们是操作的是同一个寄存器。
只有在寄存器空闲的时候,才能写入数据。
也就是说,我们在中断/轮询处理过程中,先把这个寄存器的值取出。
然后再写入我们要发送的数据。
中断允许寄存器(IER):
8250有四级中断。
分别为:
接收出错中断。
接收寄存器满中断。
发送寄存器空中断。
Moden状态改变中断。
四种中断都对应寄存器的1个位。
剩余四位无意义,为0。
从这里可以看到,发送数据并不是每次都要等接收中断来了,才能发送。
也可以在发送寄存器空的中断处理中发送。
中断标识寄存器(IIS):
既然8250对应有四种中断。
那中断过来之后,怎么区分是哪种类型的中断呢?
这就是中断标识寄存器的作用。
IIS中有表示中断状态的对应位。
都是相对于IER来说的。
另外,与IER的高四位始终为0的情况相反的是,IIS的前二位能够驱分芯片的类型。
如:
8250的IIS前二位为00。
而16550A芯片的IIS前二位为11。
这里顺带提一下8250和16550A的区别:
8250每次只能接收或者发送1个字节。
,而16550A则提供了一个FIFO缓存区。
有16个字节的缓存空间。
这样就可以减轻CPU的负担。
在16550A中,新增了一个寄存器FIFO控制寄存器(FCR).其中有一个标识可以启用或者禁用FIFO。
线路控制寄存器(LCR):
可以用来控制数据的传输方式。
比如说设置奇偶检验位,数据位的长度,停止位等。
Moden控制寄存器(MCR):
用来向moden发送RTS/DTR信号。
在这个寄存器中还要注意有二个特殊的位。
一个是自检位。
该位被置之后,数据在8250的内部移动。
可以用来检测8250数据接收与发送功能。
另外的一个位是中断允许位。
它允许8250向CPU产生中断.
线路状态寄存器(LSR):
用来检查线路的状态。
例如数据包出错原因,发送寄存器/接收寄存器空状态标识以及终止符检测。
Moden状态寄存器检测(MSR):
用来采集moden的状态。
例如DSR和CTS信号检测。
载波检测等.
除法寄存器:
一般用来设定波特率:
它是低8位在发送/读取寄存器。
高8位在IER寄存器,通过LCR有最高位来控制除法寄存器是否可用。
三:
驱动代码
Kernel版本:
2.6.25
代码:
见附件
调试:
可以用minicom,选择设备节点为:
/dev/8250X(主串口为0,次串口为1)。
如果你没有配置udev或者hotplug,就需要手动在/dev/下mknod相关结点。
附运行时候的截图:
Makefile
#Comment/uncommentthefollowinglinetodisable/enabledebugging
#DEBUG=y
#Addyourdebuggingflag(ornot)toCFLAGS
ifeq($(DEBUG),y)
DEBFLAGS=-O-g-DSCULL_DEBUG#"-O"isneededtoexpandinlines
else
DEBFLAGS=-O2
endif
#CFLAGS+=$(DEBFLAGS)
#CFLAGS+=-I..
ifneq($(KERNELRELEASE),)
#callfromkernelbuildsystem
obj-m:
=serial_driver.o
else
KERNELDIR?
=/lib/modules/$(shelluname-r)/build
PWD:
=$(shellpwd)
default:
$(MAKE)-C$(KERNELDIR)M=$(PWD)modules
endif
clean:
rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versionsmodule*Module*
depend.dependdep:
$(CC)$(CFLAGS)-M*.c>.depend
ifeq(.depend,$(wildcard.depend))
include.depend
endif
源代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ericxiao:
xgr178@");
#defineSERIAL8250_TYPE99
inlinestaticvoidserial8250_stop_send(structuart_port*port);
spinlock_tmy_lock=SPIN_LOCK_UNLOCKED;
enumcheck_type
{
odd_check,
even_check,
no_check,
};
//#defineSER_DBG1
#defineDEF_baudrate9600
#defineDEF_databit8
#defineDEF_stopbit1
#defineDEF_checkno_check
#defineARRY_SIZE(arry)sizeof((arry))/sizeof((arry)[0])
#ifSER_DBG
#defineSer_dbg(args...)printk(args)
#else
#defineSer_dbg(args...)
#endif
staticunsignedlongbaudrate_arry[]={110,300,600,1200,2400,4800,9600,14400,19200,38400,57600,115200,230400,380400,460800,921600};
staticintbaudrate=DEF_baudrate;
staticintcheck=DEF_check;
staticintstopbit=DEF_stopbit;
staticintdatabit=DEF_databit;
enumreg_type
{
RBR=0,
THR=0,
DIVR_L=0,
DIVR_H=1,
IER=1,
IIR,
LCR,
MCR,
LSR,
MSR,
FIFO,
MAX_REG,
};
//LCRbit
#defineLCR_DIVENABLEBIT7
#defineLCR_DATABIT0
#defineLCR_STOPBIT2
#defineLCR_CHECKENABLE3
#defineLCR_CHECKBIT4
//IERbit
#defineIER_RECEVEINTE0
#defineIER_SENTEMPTYINTE1
#defineIER_LINESTATAINTE2
#defineIER_MODESTATAINTE3
//MCRbit
#defineMCR_SELFTEST4
#defineMCR_INTRENABLE3
//IIRbit
#defineIIR_ISINTR0
#defineIIR_INTERIDENT1
staticirqreturn_tserial8259_intr(intirqno,void*dev_id)
{
u8reg_data=0;
intcount=0;
unsignedlongsave;
structuart_port*port=(structuart_port*)dev_id;
u32database=port->iobase;
charc;
inttail;
inthead;
if(port->irq!
=irqno)
returnIRQ_NONE;
while
(1)
{
reg_data=inb(database+IIR);
if(reg_data&(1<{
Ser_dbg("Nothaveinterrupt.\n");
break;
}else
{
switch((reg_data&(0x03<>IIR_INTERIDENT)
{
case0:
Ser_dbg("Modenstatechange.\n");
reg_data=0;
reg_data=inb(database+MSR);
break;
case1:
Ser_dbg("Sentbufferempty.\n");
//spin_lock_irqsave(&port->lock,save);
tail=port->info->xmit.tail;
//ifthebufferisempty
if(!
uart_circ_empty(&port->info->xmit))
{
head=port->info->xmit.head;
c=port->info->xmit.buf[tail];
tail=(tail+1)&(UART_XMIT_SIZE-1);
port->info->xmit.tail=tail;
outb(c,database+THR);
Ser_dbg("input:
%c\n",c);
}else{
serial8250_stop_send(port);
Ser_dbg("bufferempty.\n");
}
//spin_unlock_irqrestore(&port->lock,save);
break;
case2:
Ser_dbg("Recvebufferfull.\n");
reg_data=0;
reg_data=inb(database+RBR);
uart_insert_char(port,0,0,reg_data,0);
tty_flip_buffer_push(port->info->tty);
//printk("%c",reg_data);
break;
case3:
Ser_dbg("Recvedataerron.\n");
reg_data=0;
reg_data=inb(database+LSR);
break;
}
}
}
returnIRQ_HANDLED;
}
inlinestaticvoidserial8250_start_send(structuart_port*port)
{
u8reg_data;
u32database=port->iobase;
reg_data=inb(database+IER);
reg_data|=1<outb(reg_data,database+IER);
return;
}
inlinestaticvoidserial8250_stop_send(structuart_port*port)
{
u8reg_data;
u32database=port->iobase;
reg_data=inb(database+IER);
reg_data&=~(1<outb(reg_data,database+IER);
return;
}
inlinestaticvoidserial8250_set_enableinter(structuart_port*port)
{
u8reg_data;
u32database=port->iobase;
//enableintrrupte.allowallinterrupt,exceptsentbufferemptyinterrupt
reg_data=0;
reg_data|=1<reg_data|=1<reg_data|=1<outb(reg_data,database+IER);
Ser_dbg("IER:
%0x.\n",reg_data);
//enablesentinterruptesingleto8259
reg_data=inb(database+MCR);
reg_data&=~(1<reg_data|=1<reg_data|=3;
outb(reg_data,database+MCR);
return0;
}
inlinestaticintserial8250_set_dataformat(structuart_port*port,intdatabit,intstopbit,intcheck)
{
u8reg_data=0;
u8data_bit=0;
u32database;
database=port->iobase;
switch(databit)
{
case5:
data_bit=0<break;
case6:
data_bit=1<break;
case7:
data_bit=2<break;
case8:
data_bit=3<break;
default:
Ser_dbg("setdatabit(%d)isnotallowed.\n",databit);
return-1;
}
reg_data|=data_bit;
//setstopbit
if(stopbit==1)
reg_data&=~(1<//setodd/evencheck
if(check==no_check)
reg_data&=~(1<else
{
if(check==odd_check)
reg_data&=~(1<elseif(check==even_check)
reg_data|=1<reg_data|=1<}
outb(reg_data,database+LCR);
return0;
}
inlinestaticintserial8250_set_baudrate(structuart_port*port,unsignedlongbaudrate)
{
intret=-1;
inti;
u16divnum;
u8div_l;
u8div_h;
u8reg_data=0;
u32database;
database=port->iobase;
for(i=1;i{
if(baudrate_arry[i]==baudrate)
ret=0;
}
if(ret==0)
{
divnum=1843200/(16*baudrate);
div_l=*(u8*)(&divnum);
div_h=*(u8*)((u8*)&divnum+sizeof(u8));
Ser_dbg("divnum=%0x;div_l=%0x;div_h=%0x.\n",divnum,div_l,div_h);
//setlcrbit7=1
reg_data=inb(database+LCR);
reg_data|=(1<outb(reg_data,database+LCR);
outb(div_l,database+DIVR_L);
outb(div_h,database+DIVR_H);
reg_data&=~(1<outb(reg_data,database+LCR);
}
returnret;
}
inlinestaticintserial8250_init(structuart_port*port)
{
u16divnum;
u8div_l;
u8div_h;
u8reg_data;
u8data_bit=0;
u32database=port->iobase;
if(!
request_region(database,MAX_REG,"8250port"))
{
printk("8250requestregionerron.\n");
return-1;
}
//clearFIFOreg
reg_data=0;
outb(reg_data,database+FIFO);
if(serial8250_set_baudrate(port,baudrate)||serial8250_set_dataformat(port,databit,stopbit,check))
gotoerron;
serial8250_set_enableinter(port);
//resetMSRLSRRBRregiset
inb(database+MSR);
inb(database+LSR);
inb(database+RBR);
return0;
erron:
printk("serial8250configureerron.\n");
return-1;
}
voidserial8250_config_port(structuart_port*port,intline)
{
Ser_dbg("serial8250configport...\n");
port->type=SERIAL8250_TYPE;
serial8250_init(port);
return;
}
voidserial8250_release_port(structuart_port*port)
{
Ser_dbg("serial8250_release_port...\n");
return;
}
voidserial8250_set_mctrl(structuart_port*port,unsignedintmctrl)
{
Ser_dbg("serial8250_set_mctrl...\n");