星期一, 十二月 03, 2007

一个Linux下的char型设备的驱动

        这个驱动程序是典型char型设备驱动,它涉及到了如何使用共享中断、相同功能的设备共用驱动程序等一些技巧。个人的愚见,欢迎来讨论。
/****************************************************************************
    直放站 UART3 driver
    shanghai xinmin telecom std.
    diming.feng 2007-05
*****************************************************************************/
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/errno.h>
#include <asm/delay.h>
//#include <asm/fcntl.h>
#include <asm/arch/irqs.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/serial_reg.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include "expuart.h"

#define GPIO_CFG0  VPint(0xFFF83000)
#define GPIO_DIR0  VPint(0xFFF83004)
#define GPIO_DATAOUT0 VPint(0xFFF83008)

#define GPIO_CFG5  VPint(0xFFF83050) 
#define GPIO_DIR5  VPint(0xFFF83054)
#define GPIO_DATAOUT5 VPint(0xFFF83058)

#define GPIO_XICFG  VPint(0xFFF83074)
#define GPIO_XISTATUS VPint(0xFFF83078)

#define UART_TX   VPchar(COM_TX_3)
#define UART_RX   VPchar(COM_RX_3)
#define UART_DLL  VPchar(COM_DLL_3)
#define UART_DLM  VPchar(COM_DLM_3) 
#define UART_IER  VPchar(COM_IER_3)
#define UART_IIR  VPchar(COM_IIR_3)
#define UART_FCR  VPchar(COM_FCR_3)
#define UART_LCR  VPchar(COM_LCR_3) 
#define UART_MCR  VPchar(COM_MCR_3) 
#define UART_LSR  VPchar(COM_LSR_3) 
#define UART_MSR  VPchar(COM_MSR_3) 
#define UART_TOR  VPchar(COM_TOR_3)

#define LSR_TE   0x40
#define LSR_THRE  0x20 
#define LSR_RFDR  0x01

#define nIRQ2   31
#define uart3_IRQ  12

#define NOIRQ   0x01
#define RSTIRQ   0x06 
#define RFVIRQ   0x04
#define RFTIRQ   0x0C
#define THRIRQ   0x02
#define MSTIRQ   0x00

#define RX_FIFO_LEVEL 14
#define UART_FCR_LEVEL  0xc0


#define CMD_BANDRATE 0   // set bandrate
#define CMD_UARTLCR  1   // set data bit
#define CMD_RXTIMEOUT 2
#define CMD_MODE  3   // set RS-485/RS-232 mode
#define CMD_MODEM_SET 4   // set internal modem

#define MAJOR_NUM  248
#define YES    1
#define NO    0
#define TIME_OUT  20000

#define uart3_int(band)   do{     \
         UART_LCR = 0; \
         UART_IER = 0; \
         UART_LCR = DLAB;\
         UART_DLL = band;\
         UART_DLM = 0; \
         UART_LCR = 0x03;\
         UART_FCR = 0x07;\ 
         UART_TOR = 0x00;\
        }while(0)

#define write_reg(reg,dat)  do{      \
         UART_TX = 0x80+reg; \
         while((UART_LSR&LSR_TE)!=LSR_TE); \
         UART_TX = dat; \
         while((UART_LSR&LSR_TE)!=LSR_TE); \
        }while(0)
        
#define read_reg(reg,dat)  do{      \
         UART_TX = reg; \
         while((UART_LSR & LSR_RFDR)!=LSR_RFDR); \
         dat = UART_RX; \
        }while(0)

static int uart3tx_busy;
static uart3_dev exp_uart[EXPUART_NUM]={
 {248, "uart3", 0},
 {247, "uart4", 1},
 {246, "uart5", 2},   // internal mode
 {245, "uart6", 3},
};

DECLARE_WAIT_QUEUE_HEAD(expuart1_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart1_queue_Wr);

DECLARE_WAIT_QUEUE_HEAD(expuart2_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart2_queue_Wr);

DECLARE_WAIT_QUEUE_HEAD(expuart3_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart3_queue_Wr);

DECLARE_WAIT_QUEUE_HEAD(expuart4_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart4_queue_Wr);

static void ms_delay(int ms)
{
 while(ms--){
  udelay(1000);
 }
}

/*
static void write_reg(char reg_addr, char reg_dat)
{
 char temp;
 
 UART_TX = 0x80+reg_addr;
 do{
  temp = UART_LSR;
 }while((temp&LSR_TE)!=LSR_TE);
 UART_TX = reg_dat;
 do{
  temp = UART_LSR;
 }while((temp&LSR_TE)!=LSR_TE);
}
*/

static int uart3_chkbuf(int write, int read, int reqnum)
{
 int temp = write-read;
 if(temp >= 0){
  if(temp >= reqnum){
   return 1;
  }
 }
 else{
  if((temp + RX_BUFF_SIZE)>= reqnum){
   return 1;
  }
 }
 return 0;
}

static void expuart_handler(int irq, void *dev_id, struct pt_regs * regs)
{
 char sub_sifr, int_no, sub_ssr;
 int  expnum;
 int  ctn;
 
 GPIO_XISTATUS = 0;
 uart3_dev *dev = (uart3_dev *)dev_id;
 expnum = dev->no;
 
 /* disable VK3214 all interrupt */
 write_reg(gIR,0x00);
 
 read_reg(gIR,int_no);   // read current interrupt uart
 int_no = int_no & 0x0f;
 if(1<<expnum != int_no){
  goto out;
 }

/* read sub uart of VK3214 interrupt flags */
 read_reg(SSR(expnum),sub_ssr);
 
 // send data to uart
 if((dev->usr_wr_num>0)&&((sub_ssr&4)==4)){
  ctn = dev->usr_wr_num - dev->tx_ctn;
  if(ctn >15){
   ctn = 15;
  }
  UART_TX = 0xc0+(expnum<<4)+(ctn-1);
  while(ctn--){
   UART_TX = dev->txbuf[dev->tx_ctn++];
  }
  while((UART_LSR & LSR_TE)!=LSR_TE);
  do{
   UART_TX = SSR(expnum);
   while((UART_LSR & LSR_RFDR)!=LSR_RFDR);
  }while((UART_RX&4)!=4);
  if(dev->tx_ctn == dev->usr_wr_num){
   write_reg(SIER(expnum),0x01);   // tx interrupt disable, rx interrupt enable
   dev->usr_wr_num = 0;
   dev->tran_flag = 1;
   uart3tx_busy = NO;
   if(exp_uart[0].wait_flag == 1){
   // printk("wake 0\n");
    exp_uart[0].wait_flag = 0;
    wake_up_interruptible(&expuart1_queue_Wr); //
   }
   if(exp_uart[1].wait_flag == 1){
   // printk("wake 1\n");
    exp_uart[1].wait_flag = 0;
    wake_up_interruptible(&expuart2_queue_Wr); //
   }
   if(exp_uart[2].wait_flag == 1){
   // printk("wake 2\n");
    exp_uart[2].wait_flag = 0;
    wake_up_interruptible(&expuart3_queue_Wr); //
   }
   if(exp_uart[3].wait_flag == 1){
   // printk("wake 3\n");
    exp_uart[3].wait_flag = 0;
    wake_up_interruptible(&expuart4_queue_Wr); //
   }
  }
  goto out;  
 }
 
 // receive data from uart
 if((sub_ssr&1) == 1){
  goto out;
 } 
 read_reg(SFSR(expnum), ctn);
 ctn = UART_RX & 0x0f;
 while(ctn--){
  read_reg(SFDR(expnum), dev->rxbuf[dev->Recv_wr_index++]);
  if(dev->Recv_wr_index == RX_BUFF_SIZE){
   dev->Recv_wr_index = 0;
  }
 }
 dev->recv_flag = uart3_chkbuf(dev->Recv_wr_index,dev->Recv_rd_index,dev->usr_rd_num);
 if(dev->recv_flag){
  switch(expnum){
   case 0:
    wake_up_interruptible(&expuart1_queue_Rd); //
    break;
   case 1:
    wake_up_interruptible(&expuart2_queue_Rd); //
    break;
   case 2:
    wake_up_interruptible(&expuart3_queue_Rd); //
    break;
   case 3:
    wake_up_interruptible(&expuart4_queue_Rd); //
    break;
   default:
    break;
  }
 }
out:
 write_reg(gIR,0xf0);
}
                         
ssize_t uart3_read(struct file *flip, char *buf, size_t count, loff_t *offset)
{     
 int i;        
 int expnum;
 wait_queue_t wait;
 uart3_dev *dev = (uart3_dev *)flip->private_data;
         
 expnum = dev->no;
 dev->usr_rd_num = count;
 dev->recv_flag = uart3_chkbuf(dev->Recv_wr_index,dev->Recv_rd_index,dev->usr_rd_num);
 if(dev->recv_flag==0){
  init_waitqueue_entry(&wait, current); //
  switch(expnum){
   case 0:
    add_wait_queue(&expuart1_queue_Rd, &wait); //
    break;
   case 1:
    add_wait_queue(&expuart2_queue_Rd, &wait); //
    break;
   case 2:
    add_wait_queue(&expuart3_queue_Rd, &wait); //
    break;
   case 3:
    add_wait_queue(&expuart4_queue_Rd, &wait); //
    break;
   default:
    break;
  }  
  set_current_state(TASK_INTERRUPTIBLE);
  schedule_timeout(dev->time_out*HZ); 
  set_current_state(TASK_RUNNING);
  switch(expnum){
   case 0:
    remove_wait_queue(&expuart1_queue_Rd, &wait); //
    break;
   case 1:
    remove_wait_queue(&expuart2_queue_Rd, &wait); //
    break;
   case 2:
    remove_wait_queue(&expuart3_queue_Rd, &wait); //
    break;
   case 3:
    remove_wait_queue(&expuart4_queue_Rd, &wait); //
    break;
   default:
    break;
  }  
 }
 count = dev->Recv_wr_index - dev->Recv_rd_index;
 if(count<0){
  count += RX_BUFF_SIZE;
 }
 if(count > dev->usr_rd_num){
  count = dev->usr_rd_num; 
 }
 dev->usr_rd_num = 0;
 for(i=0; i<count; i++){
  buf[i] = dev->rxbuf[dev->Recv_rd_index];
  dev->Recv_rd_index += 1;
  if(dev->Recv_rd_index == RX_BUFF_SIZE){
   dev->Recv_rd_index = 0;
  }
 }
 return count;
}

static int uart3_write(struct file *flip, int *buf, size_t count, loff_t *offset)
{
 int expnum;
 wait_queue_t wait;
 uart3_dev *dev = (uart3_dev *)flip->private_data;
 
 if(count==0){
  return 0;
 }
 dev->tx_ctn = 0;
 expnum = dev->no;
 if((dev->tran_flag==1)&&(uart3tx_busy==NO)){
  goto copy_data;
 }
// printk("wait %d\n",expnum);
 dev->wait_flag = 1;
 init_waitqueue_entry(&wait, current); //
 switch(expnum){
  case 0:
   add_wait_queue(&expuart1_queue_Wr, &wait); //
   break;
  case 1:
   add_wait_queue(&expuart2_queue_Wr, &wait); //
   break;
  case 2:
   add_wait_queue(&expuart3_queue_Wr, &wait); //
   break;
  case 3:
   add_wait_queue(&expuart4_queue_Wr, &wait); //
   break;
  default:
   break;
 } 
 set_current_state(TASK_INTERRUPTIBLE);   
 schedule();
 set_current_state(TASK_RUNNING);
 switch(expnum){
  case 0:
   remove_wait_queue(&expuart1_queue_Wr, &wait); //
   break;
  case 1:
   remove_wait_queue(&expuart2_queue_Wr, &wait); //
   break;
  case 2:
   remove_wait_queue(&expuart3_queue_Wr, &wait); //
   break;
  case 3:
   remove_wait_queue(&expuart4_queue_Wr, &wait); //
   break;
  default:
   break;
 }
copy_data:
 if(count > TX_BUFF_SIZE){
  count = TX_BUFF_SIZE;
 }
 copy_to_user(dev->txbuf, buf, count);
 dev->tran_flag = 0;
 dev->usr_wr_num = count;
 uart3tx_busy = YES;
 write_reg(SIER(expnum),0x03);   // rx and tx interrupt enable
 return count;
}

static int uart3_open(struct inode *inode,struct file* flip)
{
 int result;
 int expnum;
 uart3_dev *dev;
 
 expnum = 248 - MAJOR(inode->i_rdev);
 dev =(uart3_dev *)(&exp_uart[expnum]);
 result = request_irq(nIRQ2, expuart_handler, SA_SHIRQ, dev->dev_name, dev);
 if(result == -1){
  printk("register the expand uart irq failed!\n");
  return -EIO;
 }
 
 dev->no = expnum;
 dev->Recv_wr_index = 0;
 dev->Recv_rd_index = 0;
 dev->usr_rd_num = 0;
 dev->recv_flag = 0;
 dev->time_out = 10;
 
 dev->tx_ctn = 0;
 dev->usr_wr_num = 0;
 dev->tran_flag = 1;
 
 MOD_INC_USE_COUNT;
 flip->private_data = dev;
 
 // default
 write_reg(SCTLR(expnum),B1200+0x08); // enable uart
 write_reg(SFOCR(expnum),0xff);  
 write_reg(SFOCR(expnum),0x0c);
 write_reg(SIER(expnum),0x01);   // rx interrupt enable, tx interrupt disable
 return 0;
}

static int uart3_close(struct inode* i,struct file* flip)
{
 uart3_dev *dev = (uart3_dev *)flip->private_data;
 
 free_irq(nIRQ2, dev);
 MOD_DEC_USE_COUNT;
 return 0;
}

static int uart3_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg)
{
 char temp;
 int  expnum;
 uart3_dev *dev = (uart3_dev *)flip->private_data;
 
 expnum = dev->no;
 
 switch(cmd)
 {
  case CMD_BANDRATE:  // set bandrate
   if(arg==115200)
    temp = B115200;
   else if(arg==57600)
    temp = B57600;
   else if(arg==38400)
    temp = B38400;
   else if(arg==19200)
    temp = B19200;
   else if(arg==9600)
    temp = B9600;
   else if(arg==4800)
    temp = B4800;
   else if(arg==2400)
    temp = B2400;
   else if(arg==1200)
    temp = B1200;
   else
    temp = B9600;
  // printk("set expuart %d bandrate:%d\n",expnum,arg);
   write_reg(SCTLR(expnum),temp+0x08); // enable uart
   break;
  case CMD_UARTLCR:  // set uart
   write_reg(SCONR(expnum),(char)arg);
   break;
  case CMD_RXTIMEOUT:
   dev->time_out = arg;
   break;
  case CMD_MODEM_SET:
   if((arg == 1)&&(expnum==2)){
    GPIO_DATAOUT5 |= 0x200;
    printk("high\n");
   }
   if((arg == 0)&&(expnum==2)){
    GPIO_DATAOUT5 &= 0xFFFFFDFF;
    printk("low\n");
   }
   break;
  default:
   break;
 }
 return 0;
}

struct file_operations uart3_fops =
{
 owner:  THIS_MODULE,
 open:  uart3_open,
 write:  uart3_write,
 read:  uart3_read,
 ioctl: uart3_ioctl,
 release: uart3_close,
};

static int __init uart3_init(void)
{
 int i;
 char temp;
 int result;
 int cfg;
 int time,time1;

 time1 = 1;
 cfg = GPIO_CFG0;
 cfg &= 0xFFFFFC00;
 cfg |= 0x3C2;   // GPIO.0=nIRQ2, GPIO.1=output, GPIO.3=TXD3,GPIO.4=RXD3
 GPIO_CFG0 = cfg;
 GPIO_DIR0 |= 0x60006;
 GPIO_DATAOUT0 |= 0x4;
 cfg = GPIO_XICFG;
 cfg &= 0xf0;
// cfg |= 0x8;    // low level sensitive
 cfg |= 0xa;    // negative edge triggered
 GPIO_XICFG = cfg;
 
 cfg = GPIO_CFG5;  // GPIO.14=output
 cfg &= 0x3F3FFFF;
 GPIO_CFG5 = cfg;
 GPIO_DIR5 = 0x2000200; 
 GPIO_DATAOUT5 |= 0x200;
  
 uart3_int(96);     // default 9600
 
 GPIO_DATAOUT0 |= 0x02;    // gpio.1=1
 ms_delay(10);
 GPIO_DATAOUT0 &= ~(0x02);   // gpio.1=0
 ms_delay(20);
 GPIO_DATAOUT0 |= 0x02;    // gpio.1=1
 write_reg(gIR,0xf0);   // enable all uart port interrupt

loop:
 uart3_int(96);     // default 9600
 write_reg(gMUCR,B115200);  // main uart bandrate 115200
 uart3_int(6);     // 230400bps@15MHz
 for(i=0;i<time1;i++){
  ms_delay(10);
 }
 time = TIME_OUT;
 UART_TX = gMUCR;
 do{
  temp = UART_LSR;
  time--;
  if(time == 0){
   time1++;
   goto loop;
  }
 }while((temp & LSR_RFDR)!=LSR_RFDR);
 temp = UART_RX;
 if(temp != B115200){
  time1++;
//  printk("temp = %02x\n",temp);
  goto loop;
 }
 
 for(i=0;i<EXPUART_NUM;i++){ 
  result = register_chrdev(exp_uart[i].major_num,exp_uart[i].dev_name,&uart3_fops);
  if(result == 0)
   printk("expand uart %d at 0xfff80300 (irq = 31) is a W90N745!\n",i+1);
 }
 uart3tx_busy = NO;
 return 0;
}

static void uart3_release(void)
{
 int i;
 
 for(i=0;i<EXPUART_NUM;i++){ 
  unregister_chrdev(exp_uart[i].major_num,exp_uart[i].dev_name);
 }
 printk("cancel the device expand uart1,2,3,4!\n");
 return;
}

module_init(uart3_init);
module_exit(uart3_release);
      

5 意見:

匿名 说...

zEpa GHD Flat Iron
yUex nfl jerseys
cHfl botas ugg
3kQkr ghd hair straighteners
9jSml ghd pink

匿名 说...

5vRjl ghd hair straightener
aQtd cheap uggs
nEbg michael kors purses
5qHmm GHD Hair Straightener
9uPas burberry usa
7gPwd chaussures ugg
7zRpr ghd nz sale
3dVre louis vuitton outlet
7hIkj michael kors handbags
8fRxe cheapest ghd
8aNhx cheap ugg boots
8aRgi discount nfl jerseys
1uJha michael kors bags
2dFux GHD Pas Cher
6gDqc cheap uggs

匿名 说...

aOco ghd hair straighteners
eMwz ugg boots uk
gCtw michael kors sale
3wUkm GHD
2dSqc burberry store
2rMhd bottes ugg pas cher
7tDji ghd
3kXxm louis vuitton bags
8yWkj Michael Kors
0lIox cheap ghd straighteners
9wKbu ugg boots cheap
8sYnr nfl football jerseys
3mPpw michael kors outlet online
2iFlb ghd lisseur
5uOha ugg boots

匿名 说...

[url=http://loans.legitpaydayloansonline1.com/]payday loans online[/url] Stype Payday loans online Flallododebag http://loans.legitpaydayloansonline1.com/ Fundpopog For getting financial help specific grace related with eradicating emergencies no fax guaranteed payday loan?Once you fill the form, the approval just is a you on this, bringing the total to pay back up to 325!

匿名 说...

http://www.cafb29b24.org/docs/buyativan/#56391 ativan and alcohol death - ativan generic name classification