Skip to content
Snippets Groups Projects
dev.c 3.87 KiB
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/delay.h>

#include <asm/mach/map.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#define UART_MAJOR_NUMBER	504
#define UART_DEV_NAME		"uart_ioctl"

#define GPIO_BASE_ADDR 0x3F200000
#define GPFSEL1 0x04
#define USRT_BASE_ADDR 0x3F201000
#define UART_DR 0x00
#define UART_FR 0x18
#define UART_CR	0x30
#define UART_LCRH 0X2C

#define IOCTL_MAGIC_NUMBER 'l'
#define IOCTL_CMD_DIRECTION		_IOWR(IOCTL_MAGIC_NUMBER, 0, int)
#define IOCTL_CMD_RECEIVE			_IOWR(IOCTL_MAGIC_NUMBER, 1, int)
#define IOCTL_CMD_TRANSMIT			_IOWR(IOCTL_MAGIC_NUMBER, 2, int)

static void __iomem *gpio_base;
static void __iomem *usrt_base;
volatile unsigned int *gpsel1;
volatile unsigned int *uart_dr;
volatile unsigned int *uart_fr;
volatile unsigned int *uart_cr;
volatile unsigned int *uart_lcrh;

int uart_open(struct inode *inode, struct file *filp){
	printk(KERN_ALERT "UART driver open!!\n");
	
	gpio_base = ioremap(GPIO_BASE_ADDR, 0x60);
	usrt_base = ioremap(USRT_BASE_ADDR, 0x60);
	gpsel1 = (volatile unsigned int *)(gpio_base + GPFSEL1);
	uart_dr = (volatile unsigned int *)(usrt_base + UART_DR);
	uart_fr = (volatile unsigned int *)(usrt_base + UART_FR);
	uart_cr = (volatile unsigned int *)(usrt_base + UART_CR); // 185PAGE
	uart_lcrh = (volatile unsigned int *)(usrt_base + UART_LCRH);
	
	
	
	return 0;
}

int uart_release(struct inode *inode, struct file *filp){
	printk(KERN_ALERT "UART driver closed!!\n");
	iounmap((void *)gpio_base);
	return 0;
}

long uart_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	unsigned int txff, rxfe;
	int kbuf=-1;
	int i=0;
	char cbuf[1024];
	int writelen = 0;
	int singlechar = '\0';
	
	switch (cmd){
		case IOCTL_CMD_DIRECTION:
			copy_from_user(&kbuf,(const void*)arg, 4);
			*gpsel1 |= (1<<14); // gpio14 takes alt0 TXD0
			*gpsel1 |= (1<<17); // gpio15 takes alt0 RXD0
			if(kbuf==0){
				// input
				*uart_cr &= ~(1<<0); // disable uart.
				*uart_lcrh &= ~(1<<4); // FEN <- 0.
				*uart_cr &= ~(1<<8); // TXE <- 0
				*uart_cr |= (1<<9); // RXE <- 1
				*uart_lcrh |= (1<<4); // FEN <- 1.
				*uart_cr |= (1<<0); // enable uart.
			} else if(kbuf==1){
				// output
				*uart_cr &= ~(1<<0); // disable uart.
				*uart_lcrh &= ~(1<<4); // FEN <- 0.
				*uart_cr &= ~(1<<9); // RXE <- 0
				*uart_cr |= (1<<8); // TXE <- 1
				*uart_lcrh |= (1<<4); // FEN <- 1.
				*uart_cr |= (1<<0); // enable uart.
			} else{
				// error.
				printk(KERN_ALERT "ERR in DIRECTION: invalid operand\n");
			}
			break;
		
		case IOCTL_CMD_RECEIVE:
			printk(KERN_ALERT "receive called\n");
			/*
			rxfe = 0;
			while(!rxfe){
				rxfe = (*uart_fr>>4); // read RXFE
			} // now rx is not empty
			*/
			singlechar = (*uart_dr); // read DATA
			copy_to_user((void*)arg, (const void*)&singlechar, 4);
			break;
			
		case IOCTL_CMD_TRANSMIT:
			printk(KERN_ALERT "transmit called\n");
			copy_from_user(&cbuf,(const void*)arg, 1024);
			writelen = strlen(cbuf);
			for(i=0; i<writelen; i++){
				singlechar = cbuf[i];
				/* 
				txff = 1;
				while(txff){
					txff = (*uart_fr>>5); // read TXFF
				} // now tx fifo is not full
				* */
				*uart_dr |= (int) singlechar; // write DATA
			}
			break;

	}
	return 0;
}

static struct file_operations uart_fops = {
	.owner = THIS_MODULE,
	.open = uart_open,
	.release = uart_release,
	.unlocked_ioctl = uart_ioctl
};

int __init uart_init(void){
	if(register_chrdev(UART_MAJOR_NUMBER, UART_DEV_NAME, &uart_fops) < 0 )
		printk(KERN_ALERT "UART driver initialization failed\n");
	else
		printk(KERN_ALERT "UART driver initialization successful\n");
	
	return 0;
}

void __exit uart_exit(void){
	unregister_chrdev(UART_MAJOR_NUMBER, UART_DEV_NAME);
	printk(KERN_ALERT "UART driver exit done\n");
}

module_init(uart_init);
module_exit(uart_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Haeram");
MODULE_DESCRIPTION("des");