⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 drv_serial.c

📁 at91rm9200 linux串口驱动源代码
💻 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 + -