一个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
发表评论