📄 drv_serial.c
字号:
/*********************************************************
用于UART0的串口通讯
发送,用队列方式,接受阻赛接收
应用中驱动可以一直处于打开状态
更新日志:
2004-11-16:
在recvbuff的基础上增加queue
设备加载后便一直在接收数据,在中断程序中将数据复制到queue.
读取调用先从queue中读取数据,如有返回实际缓冲数据,
若无,等待接收中断, 返回实际数据.
IOCTRL增加等待时间,如为0则无限等待.
2004-11-17:发送通过示波器测试
2004-11-18:通过了主机间多进程通讯测试
*********************************************************/
#include "../define.h"
#include "queue.h"
#include "queue.c"
#define MEMBER_MASK 0X07
// ????MAX_Q_UNIT 当其设为1是有毛病
#define MAX_Q_UNIT 50
#define RECVBUFFSIZE 20
//#define SLEEPTIMEOUT 0.1
#define READURAT0TIMEOUT 2
DECLARE_WAIT_QUEUE_HEAD (us1_sendq);
DECLARE_WAIT_QUEUE_HEAD (us1_recvq);
//******** 待调参数 ***************************
//*********** 串口1***********
/*
#define UART0base AT91C_BASE_US1
#define UART0PID 7
#define UART0PIO AT91C_BASE_PIOB
#define UART0TX AT91C_PB20_TXD1
#define UART0RX AT91C_PB21_RXD1
#define UART0Is_P_BSR 0
*/
//****************************
//*********** 串口3***********
#define UART0base AT91C_BASE_US3
#define UART0PID 9
#define UART0PIO AT91C_BASE_PIOA
#define UART0TX AT91C_PA5_TXD3
#define UART0RX AT91C_PA6_RXD3
#define UART0Is_P_BSR 1
//****************************
//*****************************
typedef struct
{
int AppFlag;
unchar RecvIsBuzy;
uint waitrecvtime;
int Ps2AllRecvCnt;
unchar sendbuff[MAX_Q_UNIT];
unchar recvbuff[RECVBUFFSIZE] ;
t_queue RecvQue;
}t_sendqps2;
static t_sendqps2 * psendq_ps2;
// static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
static ssize_t write_ps2(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
uint thissend;
uint trycnt;
int left=count;
AT91S_USART *pbase = UART0base;
while(pbase->US_TCR) //如果有数据在发送中,请等待
{
current->counter>>=2; //by zdq 20050111
schedule();
//printk("wait..");
}
while(left > 0)
{
thissend=(left > MAX_Q_UNIT)? MAX_Q_UNIT:left ;
copy_from_user(psendq_ps2->sendbuff,&buf[count - left],thissend);
if (thissend==1)
{ //等待其空
trycnt=0;
while((pbase->US_CSR&AT91C_US_TXRDY)==0)
{
if (trycnt++ > 0x100000)
{
printk("wait AT91C_US_TXRDY timeout:TCR:%d",pbase->US_CSR);
break;
}
}
pbase->US_THR=psendq_ps2->sendbuff[0];
}
else
{
pbase->US_TPR = virt_to_bus(psendq_ps2->sendbuff);
pbase->US_TCR = thissend;
pbase->US_IER = AT91C_US_ENDTX;
// //printk("write sleep..[ buff:%s ,US_RCR:%d ;]",jiffies,psendq_ps2->sendbuff,pbase->US_RCR );
// interruptible_sleep_on(&us1_sendq); //SLEEPTIMEOUT
interruptible_sleep_on_timeout(&us1_sendq,HZ*5); //SLEEPTIMEOUT
}
left -= thissend;
// printk("ThisSend:%d left:%d",thissend,left);
}
return count;
}
void ps2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
uint status;
uchar ch;
uint recvcnt ,i;
uint IMR_tmp;
AT91S_USART *pbase = UART0base;
AT91PS_PIO pPio = UART0PIO ;
uint *pint =(int *)psendq_ps2->recvbuff;
//出错处理和接收结束
status = pbase->US_CSR;
if (status & ( AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE ))
{
pbase->US_CR = AT91C_US_RSTSTA; //复位状态位
}
if (status & (AT91C_US_RXRDY|AT91C_US_TIMEOUT))
{
if (status & AT91C_US_RXRDY)
{
ch = pbase->US_RHR;
ToQueue_KeepNewPs2(&psendq_ps2->RecvQue,ch);
}
else
{
pbase->US_CR = AT91C_US_STTTO ; //reload timeout
}
if ( psendq_ps2->RecvIsBuzy)
{
if( (!(++psendq_ps2->Ps2AllRecvCnt & 0x0f))|| (status & AT91C_US_TIMEOUT) )
{
wake_up_interruptible(&us1_recvq);
}
}
}
/*
if (status & ( AT91C_US_TIMEOUT | AT91C_US_ENDRX ))
{
recvcnt = 4;//RECVBUFFSIZE - pbase->US_RCR;
for(i=0;i<recvcnt;i++)
{
// ToQueue_KeepNewPs2(&psendq_ps2->RecvQue,psendq_ps2->recvbuff[i]);
ToQueuePs2(&psendq_ps2->RecvQue,psendq_ps2->recvbuff[i]);
}
*pint = 0xeeddddff; // for test
pbase->US_CR = AT91C_US_STTTO ; //reload timeout
pbase->US_RPR= virt_to_bus(psendq_ps2->recvbuff);
pbase->US_RCR = RECVBUFFSIZE;
if ( psendq_ps2->RecvIsBuzy)
{
wake_up_interruptible(&us1_recvq);
// printk("wakeup recv");
}
}
*/
if (( status & AT91C_US_ENDTX ) && (pbase->US_IMR & AT91C_US_ENDTX))
{
pbase->US_IDR=AT91C_US_ENDTX;
wake_up_interruptible(&us1_sendq);
// printk(" -Int send- ");
}
}
/*****************************************/
/* Character drivce driver */
/*****************************************/
//static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr)
static ssize_t read_ps2(struct file *file, char *buf, size_t count, loff_t *ptr)
{
uchar ch;
uint waitlong,b_jiff;
AT91S_USART *pbase = UART0base;
uint readcnt=0;
b_jiff = jiffies;
waitlong=0;
while( (waitlong < MAXSERIALRBUSY) && psendq_ps2->RecvIsBuzy)
{
printk("\nserial read wait psendq_ps2->RecvIsBuzy:[%d] ",psendq_ps2->RecvIsBuzy);
interruptible_sleep_on_timeout(&us1_recvq,5); // BY ZDQ
waitlong= jiffies - b_jiff;
}
while((readcnt < count))
{
if (FromQueuePs2(&psendq_ps2->RecvQue,&ch )==0)
{
buf[readcnt]=ch;
readcnt++;
}
else
break;
}
if (readcnt>0) //by zdq test //if (readcnt==count)
return readcnt;
else
{
readloop:
psendq_ps2->RecvIsBuzy=1;
if (psendq_ps2->waitrecvtime)
{
waitlong = interruptible_sleep_on_timeout(&us1_recvq,psendq_ps2->waitrecvtime);
if (waitlong==0)
{
return -1;
}
}
else
{
interruptible_sleep_on_timeout(&us1_recvq,HZ*8);//HZ*2
}
psendq_ps2->RecvIsBuzy=0;
if (IsQueueEmptyPs2(&psendq_ps2->RecvQue))
{
// printk(" drv serial:goto readloop ");
goto readloop;
}
while((readcnt < count))
{
if (FromQueuePs2(&psendq_ps2->RecvQue,&ch )==0)
{
buf[readcnt]=ch;
readcnt++;
}
else
break;
}
return readcnt;
}
}
int ioctl_ps2(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
AT91S_USART *pbase;
//printk("ioctrl serial\n");
pbase = UART0base;
switch(cmd)
{
case US_SET_APPFLAG:
psendq_ps2->AppFlag=arg;
return 0;
case US_GET_APPFLAG:
return psendq_ps2->AppFlag;
case US_EMPTY_R_PDC:
pbase->US_RPR = virt_to_bus(psendq_ps2->recvbuff);
pbase->US_RCR = RECVBUFFSIZE;
Init_QueuePs2(&psendq_ps2->RecvQue);
break;
case US_SET_RECV_TIMEOUT:
psendq_ps2->waitrecvtime = arg;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return 0;
}
static int open_ps2(struct inode *inode,struct file *file )
{
int result;
AT91S_USART *pbase = UART0base;
psendq_ps2->waitrecvtime = 0;
MOD_INC_USE_COUNT;
return 0;
}
static int release_ps2(struct inode *inode,struct file *file )
{
AT91S_USART *pbase = UART0base;
MOD_DEC_USE_COUNT;
psendq_ps2->RecvIsBuzy=0;
return 0;
}
struct file_operations ps2_fops = {
read: read_ps2,
write: write_ps2,
open: open_ps2,
release: release_ps2,
ioctl: ioctl_ps2,
};
__inline unsigned int AT91F_US_Baudrate (
const unsigned int main_clock, // \arg peripheral clock
const unsigned int baud_rate) // \arg UART baudrate
{
unsigned int baud_value = ((main_clock*10)/(baud_rate * 16));
if ((baud_value % 10) >= 5)
baud_value = (baud_value / 10) + 1;
else
baud_value /= 10;
return baud_value;
}
__inline void AT91F_US_SetBaudrate (
AT91PS_USART pUSART, // \arg pointer to a USART controller
unsigned int mainClock, // \arg peripheral clock
unsigned int speed) // \arg UART baudrate
{
//* Define the baud rate divisor register
pUSART->US_BRGR = AT91F_US_Baudrate(mainClock, speed);
}
__inline void AT91F_PDC_DisableTx (
AT91PS_PDC pPDC ) // \arg pointer to a PDC controller
{
pPDC->PDC_PTCR = AT91C_PDC_TXTDIS;
}
//*----------------------------------------------------------------------------
//* \fn AT91F_PDC_DisableRx
//* \brief Disable receive
//*----------------------------------------------------------------------------
__inline void AT91F_PDC_DisableRx (
AT91PS_PDC pPDC ) // \arg pointer to a PDC controller
{
pPDC->PDC_PTCR = AT91C_PDC_RXTDIS;
}
__inline void AT91F_PDC_SetNextTx (
AT91PS_PDC pPDC, // \arg pointer to a PDC controller
char *address, // \arg address to the next bloc to be transmitted
unsigned int bytes) // \arg number of bytes to be transmitted
{
pPDC->PDC_TNPR = (unsigned int) address;
pPDC->PDC_TNCR = bytes;
}
//*----------------------------------------------------------------------------
//* \fn AT91F_PDC_SetRx
//* \brief Set the receive transfer descriptor
//*----------------------------------------------------------------------------
__inline void AT91F_PDC_SetRx (
AT91PS_PDC pPDC, // \arg pointer to a PDC controller
char *address, // \arg address to the next bloc to be received
unsigned int bytes) // \arg number of bytes to be received
{
pPDC->PDC_RPR = (unsigned int) address;
pPDC->PDC_RCR = bytes;
}
__inline void AT91F_PDC_EnableRx (
AT91PS_PDC pPDC ) // \arg pointer to a PDC controller
{
pPDC->PDC_PTCR = AT91C_PDC_RXTEN;
}
__inline void AT91F_PDC_SetTx (
AT91PS_PDC pPDC, // \arg pointer to a PDC controller
char *address, // \arg address to the next bloc to be transmitted
unsigned int bytes) // \arg number of bytes to be transmitted
{
pPDC->PDC_TPR = (unsigned int) address;
pPDC->PDC_TCR = bytes;
}
__inline void AT91F_PDC_EnableTx (
AT91PS_PDC pPDC ) // \arg pointer to a PDC controller
{
pPDC->PDC_PTCR = AT91C_PDC_TXTEN;
}
__inline void AT91F_PDC_SetNextRx (
AT91PS_PDC pPDC, // \arg pointer to a PDC controller
char *address, // \arg address to the next bloc to be received
unsigned int bytes) // \arg number of bytes to be received
{
pPDC->PDC_RNPR = (unsigned int) address;
pPDC->PDC_RNCR = bytes;
}
__inline void AT91F_PDC_Open (
AT91PS_PDC pPDC) // \arg pointer to a PDC controller
{
//* Disable the RX and TX PDC transfer requests
AT91F_PDC_DisableRx(pPDC);
AT91F_PDC_DisableTx(pPDC);
//* Reset all Counter register Next buffer first
AT91F_PDC_SetNextTx(pPDC, (char *) 0, 0);
AT91F_PDC_SetNextRx(pPDC, (char *) 0, 0);
AT91F_PDC_SetTx(pPDC, (char *) 0, 0);
AT91F_PDC_SetRx(pPDC, (char *) 0, 0);
//* Enable the RX and TX PDC transfer requests
AT91F_PDC_EnableRx(pPDC);
AT91F_PDC_EnableTx(pPDC);
}
__inline void AT91F_US_SetTimeguard (
AT91PS_USART pUSART, // \arg pointer to a USART controller
unsigned int timeguard) // \arg timeguard value
{
//* Write the Timeguard Register
pUSART->US_TTGR = timeguard ;
}
int init_module(void)
{
int result;
AT91S_USART *pbase = UART0base;
AT91PS_PMC pPMC = AT91C_BASE_PMC ;
AT91PS_PIO pPio = UART0PIO ;
printk("<1>This is a kernel module for UART0.\n");
result = register_chrdev(SER_PS2_major, "UART0", &ps2_fops);
if (result < 0)
{
printk("registe UART0 OK\n");
return result;
}
pbase->US_IDR =-1;
result = request_irq(UART0PID,ps2_interrupt,SA_INTERRUPT,"ps2",NULL);
if (result)
printk("Interrupt %d assigned fail !!!!!\n",UART0PID);
else
printk("Interrupt %d assigned OK.\n",UART0PID);
if(!(psendq_ps2 = (t_sendqps2 *)kmalloc(sizeof(t_sendqps2),GFP_KERNEL)))
{
printk("kmalloc for pUS_sendq fail!!!!\n");
return -ENOMEM;
}
//初始化变量
Init_QueuePs2(&psendq_ps2->RecvQue);
//设置芯片外围
#if UART0Is_P_BSR == 1
pPio->PIO_BSR = (UART0TX|UART0RX) ;
#else
pPio->PIO_ASR = (UART0TX|UART0RX) ;
#endif
pPio->PIO_PDR = UART0TX|UART0RX ;
//允许时钟
AT91F_PMC_EnablePeriphClock(pPMC,1<<UART0PID);
AT91F_US_Configure(pbase, AT91C_MASTER_CLK,
AT91C_US_USMODE_NORMAL | AT91C_US_CHRL_8_BITS|AT91C_US_PAR_NONE,
AT91C_SERIAL_DEF_SPEED ,0);
/*
pbase->US_RPR= virt_to_bus(psendq_ps2->recvbuff);
pbase->US_RCR = RECVBUFFSIZE;
pbase->US_PTCR = AT91C_PDC_RXTEN;
*/
pbase->US_CR = AT91C_US_RXEN | AT91C_US_TXEN;
// pbase->US_IER=AT91C_US_TIMEOUT | AT91C_US_ENDRX ;
pbase->US_IER =AT91C_US_RXRDY|AT91C_US_TIMEOUT;
pbase->US_RTOR = 0x5f;
pbase->US_CR = AT91C_US_STTTO;
psendq_ps2->RecvIsBuzy=0;
psendq_ps2->Ps2AllRecvCnt=0;
return 0;
}
int cleanup_module(void)
{
AT91S_USART *pbase = UART0base;
unregister_chrdev(SER_PS2_major, "ps2");
pbase->US_IDR = AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE
| AT91C_US_TIMEOUT | AT91C_US_ENDRX | AT91C_US_ENDTX | AT91C_US_ENDTX;
free_irq(UART0PID,NULL);
kfree(psendq_ps2);
printk("<1>Cleanup kernel module OK.\n");
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -