Linux device drivers学习笔记(5)——uart primary driver code

来源:互联网 发布:unity3d塔防模型 编辑:程序博客网 时间:2024/05/10 20:57

这是第一阶段的uart driver code,初步的性能还算可以,以后继续完善。平台是S3C2440,Linux的版本是2.6.22.6。

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/semaphore.h>
#include <linux/wait.h>




/*macro define*/
//module related
#define DEVICE_NAME     "uart1" 
#define UART1_MAJOR     233
//GPIO related register
#define GPIO_OFT(x) ((x) - 0x56000000)
#define GPHCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000070)))
#define GPHUP (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000078)))
//UART1 related register
#define UART_OFT(x) ((x) - 0x50004000)
#define ULCON1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004000)))
#define UCON1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004004)))
#define UFCON1   (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004008)))
#define UMCON1   (*(volatile unsigned long *)(uart_va + UART_OFT(0x5000400C)))
#define UTRSTAT1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004010)))
#define UERSTAT1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004014)))
#define UFSTAT1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004018)))
#define UMSTAT1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x5000401C)))
#define UTXH1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004020)))
#define URXH1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004024)))
#define UBRDIV1 (*(volatile unsigned long *)(uart_va + UART_OFT(0x50004028)))
//uart1 baud rate related
#define UART_CLK        50000000
#define UART_BAUD_RATE  115200      // baud rate
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)
//buffer related
#define BUFFER_SIZE 256
/*variable define*/
//buffer related
struct uart1_buffer
{
char buffer[BUFFER_SIZE + 1];
unsigned int w_position;
unsigned int r_position;
};
static struct uart1_buffer send_buffer,recv_buffer;
//ioremap related
static unsigned long gpio_va;
static unsigned long uart_va;
//irq related
struct irq_desc {
    int irq;
    unsigned long flags;
    char *name;
};
static struct irq_desc uart1_irqs[3] =
{
{IRQ_S3CUART_RX1, IRQF_TRIGGER_NONE, "UART1_RECV"},
{IRQ_S3CUART_TX1, IRQF_TRIGGER_NONE, "UART1_SEND"},
{IRQ_S3CUART_ERR1, IRQF_TRIGGER_NONE, "UART1_ERR1"},
};
static void uart1_send_do_tasklet(unsigned long unused);
static DECLARE_TASKLET(send_tasklet,uart1_send_do_tasklet,0);
//semaphore related
static struct semaphore send_irq_sem,read_sem;
//wait queue related
static DECLARE_WAIT_QUEUE_HEAD(send_queue);
static char send_flag = 0,wait_flag = 0;
static struct semaphore wait_flag_sem;
static void uart1_hardware_init(void)
{
//set up related ports
GPHCON  |= 0x0a00;// use GPH4,GPH5 as TXD1,RXD1
GPHUP   |= 0x30;// GPH4,GPH5 pull up

ULCON1  = 0x03;// 8-bits,1 stop bit,no parity,normal operation
UCON1   = 0x05;// Interrupt request mode for both TX and RX,UARTclk=PCLK
UFCON1  = 0x01;// FIFO mode,RX FIFO trigger level = 1 bytes,TX FIFO trigger level = empty
UMCON1  = 0x00;// no flow control
UBRDIV1 = UART_BRD;// baud rate = 115200
}
/*interrupt related*/
static irqreturn_t uart1_send_interrupt(int irq, void *dev_id)
{
if(send_buffer.r_position != send_buffer.w_position)
{
UTXH1 = send_buffer.buffer[send_buffer.r_position++];
//while(!(UTRSTAT1&(0x1<<1)));//wait for buffer register is empty.
if(send_buffer.r_position == BUFFER_SIZE + 1)
{
send_buffer.r_position = 0;
}
}
else
{
disable_irq(IRQ_S3CUART_TX1);
up(&send_irq_sem);
}
//schedule tasklet
tasklet_schedule(&send_tasklet);
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t uart1_recv_interrupt(int irq, void *dev_id)
{
unsigned int w_position;
w_position = recv_buffer.w_position;
recv_buffer.buffer[recv_buffer.w_position++] = URXH1;
if(recv_buffer.w_position == BUFFER_SIZE +1)
{
recv_buffer.w_position = 0;
}
if(w_position == recv_buffer.r_position)
{
up(&read_sem);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static irqreturn_t uart1_err1_interrupt(int irq, void *dev_id)
{
printk(DEVICE_NAME " err1 happened!\n");
return IRQ_RETVAL(IRQ_HANDLED);
}
void uart1_send_do_tasklet(unsigned long unused)
{
unsigned int empty_space;
if(wait_flag)
{
if(send_buffer.w_position < send_buffer.r_position)
{
empty_space = send_buffer.r_position - send_buffer.w_position - 1;
}
else
{
empty_space = BUFFER_SIZE - 1 - (send_buffer.w_position - send_buffer.r_position);
}
if(empty_space > BUFFER_SIZE/4)
{
send_flag = 1;
printk(DEVICE_NAME " writing wake up!\n");
down_interruptible(&wait_flag_sem);
wait_flag = 0;
up(&wait_flag_sem);
wake_up_interruptible(&send_queue);
}
}
else
{
send_flag = 0;
}
}
/*file_operation related*/
static int uart1_open(struct inode *inode, struct file *file)
{
int err;
/*free related irqs if they were allocated*/
free_irq(uart1_irqs[0].irq, (void *)recv_buffer.buffer);
free_irq(uart1_irqs[1].irq, (void *)send_buffer.buffer);
free_irq(uart1_irqs[2].irq, (void *)send_buffer.buffer);

err = request_irq(uart1_irqs[0].irq, uart1_recv_interrupt, uart1_irqs[0].flags, 
uart1_irqs[0].name, (void *)recv_buffer.buffer);
if(err)
{
free_irq(uart1_irqs[0].irq, (void *)recv_buffer.buffer);
printk(DEVICE_NAME " irq register failed!\n");
return err;
}
err = request_irq(uart1_irqs[1].irq, uart1_send_interrupt, uart1_irqs[1].flags, 
uart1_irqs[1].name, (void *)send_buffer.buffer);
if(err)
{
free_irq(uart1_irqs[0].irq, (void *)recv_buffer.buffer);
free_irq(uart1_irqs[1].irq, (void *)send_buffer.buffer);
printk(DEVICE_NAME " irq register failed!\n");
return err;
}
err = request_irq(uart1_irqs[2].irq, uart1_err1_interrupt, uart1_irqs[2].flags, 
uart1_irqs[2].name, (void *)send_buffer.buffer);
if(err)
{
free_irq(uart1_irqs[0].irq, (void *)recv_buffer.buffer);
free_irq(uart1_irqs[1].irq, (void *)send_buffer.buffer);
free_irq(uart1_irqs[2].irq, (void *)send_buffer.buffer);
printk(DEVICE_NAME " irq register failed!\n");
return err;
}
disable_irq(IRQ_S3CUART_TX1);
send_buffer.w_position = 0;
send_buffer.r_position = 0;
recv_buffer.w_position = 0;
recv_buffer.r_position = 0;
return err;
}
static int uart1_release(struct inode *inode, struct file *file)
{
free_irq(uart1_irqs[0].irq, (void *)recv_buffer.buffer);
free_irq(uart1_irqs[1].irq, (void *)send_buffer.buffer);
free_irq(uart1_irqs[2].irq, (void *)send_buffer.buffer);
return 0;
}
static int uart1_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
char buf_user[BUFFER_SIZE/2];
unsigned int n = 0,user_count = count,copy_n,empty_space;
if(user_count > 0)
{
copy_n = copy_from_user(buf_user,buf,user_count);//if copy_from_user successes,return 0;assumption it always successes.
user_count = user_count - copy_n;
if(send_buffer.w_position < send_buffer.r_position)
{
empty_space = send_buffer.r_position - send_buffer.w_position - 1;
}
else
{
empty_space = BUFFER_SIZE - 1 - (send_buffer.w_position - send_buffer.r_position);
}
if(empty_space < user_count)//if the empty space is not enough
{
down_interruptible(&wait_flag_sem);
wait_flag = 1;
up(&wait_flag_sem);
printk(DEVICE_NAME " writing waited!\n");
wait_event_interruptible(send_queue,send_flag == 1);
send_flag = 0;
}
while(user_count--)
{
send_buffer.buffer[send_buffer.w_position++] = buf_user[n++];
if(send_buffer.w_position == BUFFER_SIZE + 1)
{
send_buffer.w_position = 0;
}
}
}
return n;
}
static int uart1_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
char buf_user[BUFFER_SIZE/2];
unsigned int n = 0,copy_fail_n,user_count = count;
if(user_count > 0)
{
if(recv_buffer.r_position != recv_buffer.w_position)
{
while(recv_buffer.r_position != recv_buffer.w_position)
{
buf_user[n++] = recv_buffer.buffer[recv_buffer.r_position++];
if(recv_buffer.r_position == BUFFER_SIZE + 1)
{
recv_buffer.r_position = 0;
}
if(n == user_count)
{
break;
}
}
if(n)
{
copy_fail_n = copy_to_user(buf,buf_user,n);
if(copy_fail_n)//if copying to user fails, poll back
{
n = n - copy_fail_n;
while(copy_fail_n--)
{
recv_buffer.r_position--;
if(recv_buffer.r_position > BUFFER_SIZE)
{
recv_buffer.r_position = BUFFER_SIZE;
}
}
}
}
}
else
{
if(down_interruptible(&read_sem))//if buffer is empty,sleep
{
return 0;
}
}
}
return n;
}
int uart1_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case 1:
if(down_trylock(&send_irq_sem))//irq enable\disable status check
{
break;
}
else
{
if(send_buffer.r_position != send_buffer.w_position)
{
UTXH1 = send_buffer.buffer[send_buffer.r_position++];
if(send_buffer.r_position == BUFFER_SIZE + 1)
{
send_buffer.r_position = 0;
}
enable_irq(IRQ_S3CUART_TX1);
break;
}
else
{
printk(DEVICE_NAME " sending buffer is empty!\n");
up(&send_irq_sem);
return 0;
}
}
case 2:
if(send_buffer.r_position == send_buffer.w_position)
{
break;
}
else
{
return 0;
}
default:
return 0;
}
return 1;
}
static struct file_operations uart1_fops = 
{
    .owner  = THIS_MODULE,
    .open   = uart1_open,
.release= uart1_release,
.read =uart1_read,
.write = uart1_write,
.ioctl = uart1_ioctl,
};
static int uart1_init(void)
{
int ret;
gpio_va = ioremap((volatile unsigned long)0x56000000, 0x100);
if (!gpio_va)
{
return -EIO;
}
uart_va = ioremap((volatile unsigned long)0x50004000, 0x100);
if (!uart_va)
{
return -EIO;
}
ret = register_chrdev(UART1_MAJOR, DEVICE_NAME, &uart1_fops);
if (ret < 0) 
{
printk(DEVICE_NAME " can't register major number!\n");
return ret;
}
//initialize hardware related to UART1
uart1_hardware_init();
//initialize semaphore
init_MUTEX(&send_irq_sem);//initialize value is 1
init_MUTEX_LOCKED(&read_sem);//initialize value is 0
init_MUTEX(&wait_flag_sem);//initialize value is 1
printk(DEVICE_NAME " initialized!\n");
return 0;
}
static void uart1_exit(void)
{
unregister_chrdev(UART1_MAJOR, DEVICE_NAME);
iounmap((volatile unsigned long *)gpio_va);
iounmap((volatile unsigned long *)uart_va);
printk(DEVICE_NAME " was removed!\n");
}
module_init(uart1_init);
module_exit(uart1_exit);


MODULE_LICENSE("GPL");


编译用makefile:

obj-m := uart1.o
KERNELDIR := /work/system/linux-2.6.22.6
PWD       := $(shell pwd)


default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
rm -f *.mod* Module.* *.o
clean:
rm -rf *.mod* Module.* *.o *.ko

0 0