www.pudn.com > drv_serial.rar > 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;iRecvQue,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<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; 
 }