Fork Me on GitHub

源码开放学ARM

LASO - Learn ARM with Source Open

首页         目录索引         资料下载         代码下载         联系作者        
下载PDF打印版本

GPIO/UART 驱动代码实现

led 驱动

#include <linux/module.h>	// module_init
#include <asm/io.h>		// ioremap
#include <linux/fs.h>		// file_operations
#include <asm/uaccess.h>	// copy_from_user

MODULE_LICENSE("GPL");

#define MA	240

volatile int * pled;

int led_drv_open(struct inode *inode, struct file *filp)
{
	int major, minor;

	major = MAJOR(inode->i_rdev);
	minor = MINOR(inode->i_rdev);

	printk("led drv open: major %d, minor %d\n", major, minor);

	return 0;
}

ssize_t led_drv_write(struct file *filp, const char __user * buf, size_t count, loff_t *f_pos)
{
	char kbuf[128];

	//buf[count] = '\0';
	printk("led drv write %d\n", count);

//	printk("buf = %s\n", buf);
//	printk("buf at %p\n", buf);
	printk("count = %d\n", count);

//	copy_from_user(kbuf, buf, count);
	*pled = buf[0];

//	printk("kbuf = %s\n", kbuf);
//	printk("kbuf at %p\n", kbuf);

	return count;
}

int led_drv_release(struct inode *inode, struct file *filp)
{
	printk("led drv release ok!\n");

	return 0;
}

struct file_operations led_fops = 
{
	.owner = THIS_MODULE,
	.open = led_drv_open,
	.write = led_drv_write,
	.release = led_drv_release,
};

static int led_drv_init(void)
{
	int rc;

	printk("led init \n");

	pled = ioremap(0xE0200284, 4);

	//*pled = 0;
	rc = register_chrdev(MA, "akae", &led_fops);
	if (rc < 0)
	{
		printk("register failed\n");
		return -1;
	}

	printk("register char ok %d!\n", rc);

	return 0;
}

static void led_drv_exit(void)
{
	printk("led exit \n");

	//*pled = 0xF;
	unregister_chrdev(MA, "akae");
	printk("unregister char ok!\n");

	return;
}

module_init(led_drv_init);
module_exit(led_drv_exit);

课堂练习: 串口设备驱动 (char device driver)

1 基本驱动功能实现

Makefile for kernel module
基本的内核模块功能,uart_drv_init, uart_drv_exit, #include, License
串口的驱动接口:  uart_init, uart_putchar, uart_getchar (ioremap)
insmod uart_drv.ko 
	uart_drv_init -> write "hello" -> for + uart_putchar
	uart_drv_init -> read char -> write char -> echo

example code

/* uart_drv.c */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>

MODULE_LICENSE("GPL");

struct uart_sfr
{
	int ulcon;
	int ucon;
	int ufcon;
	int umcon;
	int utrstat;
	int uerstat;
	int ufstat;
	int umstat;
	int utxh;
	int urxh;
	int ubrdiv;
	int udivslot;
};

typedef struct uart_sfr USFR;

static volatile USFR *puart;

void uart_init(void)
{
#if 0
	// see how linux set UART0 regs
	puart = ioremap(0xe2900000, sizeof(USFR));
	printk("reg ulcon = %x\n", puart->ulcon);
	printk("reg ucon = %x\n", puart->ucon);
	printk("reg ubrdiv = %x\n", puart->ubrdiv);
	printk("reg udivslot = %x\n", puart->udivslot);
#endif

	puart = ioremap(0xe2900c00, sizeof(USFR));
	puart->ulcon = 0x3;
	puart->ucon = 0x7c5;
	puart->ubrdiv = 0x23;
	puart->udivslot = 0x808;

	return;
}

int uart_putchar(char c)
{
	while ((puart->utrstat & (1<<2)) == 0)
		;

	puart->utxh = c;

	return 0;
}

int uart_drv_init(void)
{
	printk("uart_drv init ok \n");

	uart_init();

	uart_putchar('h');
	uart_putchar('e');
	uart_putchar('l');
	uart_putchar('l');
	uart_putchar('o');
	uart_putchar('\n');

	return 0;
}

void uart_drv_exit(void)
{
	printk("uart_drv exit ok \n");

	return;
}

module_init(uart_drv_init);
module_exit(uart_drv_exit);

2 加入字符设备驱动的接口

加入对于串口 UART3 的 open, release, read, write

	open - 115200, 8N1 (init)
	read - return 1 (getchar())
	write - uart_putchar() 每次写入1个字节
	release - null

加入注册字符设备和注销字符设备

struct file_operations fops =
{
	...
};
init -> register_chrdev(245, "notmyttyS3", &f_ops)
exit -> unregister_chrdev(245, "notmyttyS3")

创建设备文件的节点

mknod myttyS3 c 245 3

测试字符设备驱动

test write:		echo "hello" > myttyS3
test read:		cat myttyS3

example code

/* uart_drv.c */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

#define UART_MAJOR	245

struct uart_sfr
{
	int ulcon;
	int ucon;
	int ufcon;
	int umcon;
	int utrstat;
	int uerstat;
	int ufstat;
	int umstat;
	int utxh;
	int urxh;
	int ubrdiv;
	int udivslot;
};

typedef struct uart_sfr USFR;

static volatile USFR *puart;

#define ULCON0		(puart->ulcon)
#define UCON0		(puart->ucon)
#define UBRDIV0		(puart->ubrdiv)
#define UDIVSLOT0	(puart->udivslot)
#define UTRSTAT0	(puart->utrstat)
#define UTXH0		(puart->utxh)
#define URXH0		(puart->urxh)

void uart_init(void)
{
	// set UART SFRs
	ULCON0 = 0x3;
	UCON0 = 0x245;

	// 66Mhz / (115200*16) - 1 = 0x23
	// 66Mhz / (19200*16) - 1 = 0xD5
	UBRDIV0 = 0x23;
	UDIVSLOT0 = 0x808;

	return;
}

char uart_getchar(void)
{
	char c;

	// polling receive status: if buffer is full
	while ((UTRSTAT0 & (1<<0)) == 0)
		;
	
	c = URXH0;
		
	return c;
}

void uart_putchar(char c)
{
	// polling transmit status: if buffer is empty
	while ((UTRSTAT0 & (1<<2)) == 0)
		;
	
	UTXH0 = c;
	
	return;
}

int uart_drv_open(struct inode * inode, struct file * filp)
{
	printk("uart open\n");

	uart_init();

	uart_putchar('o');
	uart_putchar('p');
	uart_putchar('e');
	uart_putchar('n');
	uart_putchar('\n');

	return 0;
}

int uart_drv_release(struct inode * inode, struct file * filp)
{
	printk("uart release\n");

	uart_putchar('c');
	uart_putchar('l');
	uart_putchar('o');
	uart_putchar('s');
	uart_putchar('e');
	uart_putchar('\n');

	return 0;
}

int uart_drv_write(struct file * filp, const char __user * buf, size_t count, loff_t *f_pos)
{
	char c;

	printk("uart write %d bytes\n", count);

	c = *buf;

	uart_putchar(c);

	return 1;
}

int uart_drv_read(struct file * filp, char __user * buf, size_t count, loff_t *f_pos)
{
	char c;

	printk("uart read %d bytes\n", count);

	c = uart_getchar();

	*buf = c;

	return 1;
}

struct file_operations uart_drv_fops = 
{
    .owner = THIS_MODULE,
    .open = uart_drv_open,
    .release = uart_drv_release,
    .write = uart_drv_write,
    .read = uart_drv_read,
};

struct cdev uart_cdev;
dev_t uart_dev_no;

int uart_drv_init(void)
{
	printk("uart_drv init ok \n");

	//register_chrdev(UART_MAJOR, "myttyS3", &uart_drv_fops);

	// use cdev
	uart_dev_no = MKDEV(UART_MAJOR, 3);
	register_chrdev_region(uart_dev_no, 1, "myttyS3");
	cdev_init(&uart_cdev, &uart_drv_fops);
	cdev_add(&uart_cdev, uart_dev_no, 1);

	return 0;
}

void uart_drv_exit(void)
{
	printk("uart_drv exit ok \n");

	//unregister_chrdev(UART_MAJOR, "myttyS3");

	// use cdev 
	unregister_chrdev_region(uart_dev_no, 1);
	cdev_del(&uart_cdev);

	return;
}

module_init(uart_drv_init);
module_exit(uart_drv_exit);

3 加入应用程序

引入系统调用, open, read, write, close
在 read 和 write 的基础上,实现简单的 shell 功能

example code

/* test.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>
#include <unistd.h>

int fd;

char mygetchar(void)
{
	char c;

	read(fd, &c, 1);

	// echo
	write(fd, &c, 1);

	return c;
}

void myputchar(char c)
{
	write(fd, &c, 1);

	return;
}

void myputs(char * s)
{
	while (*s)
		myputchar(*s++);

	return;
}

void mygets(char * s)
{
	char c;

	while ((c = mygetchar()) != '\r')
		*s++ = c;

	*s = '\0';
	myputs("\r\n");

	return;
}

int main(void)
{
	fd = open("myttyS3", O_RDWR);

	while (1)
	{
		char buf[512];

		myputs("akaedu $ ");
		mygets(buf);
		myputs(buf);
		myputs("\r\n");

	#if 0
		char c;
		c = mygetchar();
		myputchar(c);
	#endif
	}

	close(fd);

	return 0;
}

uart 驱动

#include <linux/module.h>
#include <linux/fs.h>	// register_chrdev
#include <asm/io.h>
//#include <asm/uaccess.h>

#define MA	243

MODULE_LICENSE("GPL");

struct uart_sfr
{
	int ulcon;
	int ucon;
	int ufcon;
	int umcon;
	int utrstat;
	int uerstat;
	int ufstat;
	int umstat;
	int utxh;
	int urxh;
	int ubrdiv;
	int udivslot,
};

typedef struct uart_sfr USFR;

static volatile USFR *puart;

//#define printk 		noprintk	

int noprintk(char * fmt, ...)
{
	return 0;
}

#define PRINT(x)	printk(#x " = 0x%x\n", x);

int uart_open(struct inode * inode, struct file * filp)
{
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);
	int * p;
	int i;

	printk("uart open: major %d, minor %d\n", major, minor);

	puart = ioremap(0xe2900000, sizeof(USFR));
	p = (int *)puart;

	PRINT((int)p);	

	for (i = 0; i < sizeof(USFR)/4; i++)
	{
		PRINT(*p++);	
	}

	puart->ufcon = 0;

	return 0;
}

int uart_release(struct inode * inode, struct file * filp)
{
	printk("uart release\n");

	return 0;
}

int uart_putchar(char c)
{
	while ((puart->utrstat & (1<<2)) == 0)
		;

	puart->utxh = c;

	return 0;
}

int myputchar(char c)
{
	if (c == '\n')
		uart_putchar('\r');

	uart_putchar(c);

	return 0;
}

int uart_write(struct file * filp, const char __user * buf, size_t count, loff_t *f_pos)
{
	int i;

	printk("uart write\n");

	printk("buf = %c\n", buf[0]);
	printk("count = %d\n", count);


	for (i = 0; i < count; i++)
	{
		myputchar(buf[i]);
	}


	return count;
}


int uart_drv_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg)
{
	#define PRINTD(x)	printk(#x " = %d, <%s>\n", x, __FUNCTION__);

	PRINTD(cmd);
	PRINTD((int)arg);

#define UART_SET_BUAD_RATE  0
#define UART_ENABLE_FIFO    1

	switch (cmd)
	{   
	case UART_SET_BUAD_RATE:
		uart_set_baudrate(arg);
		break;
	}   

	return 0;
}


struct file_operations uart_fops = 
{
	.owner = THIS_MODULE,
	.open = uart_open,
	.release = uart_release,
	.write = uart_write,
	.ioctl = uart_ioctl,
};

int uart_init(void)
{
	int rc;

	rc = register_chrdev(MA, "myuart", &uart_fops);

	if (rc < 0)
	{
		printk("register chrdev failed! %d\n", rc);
		return -1;
	}

	printk("uart init ok \n");

	return 0;
}

void uart_exit(void)
{
	printk("uart exit ok \n");

	unregister_chrdev(MA, "myuart");

	return;
}


module_init(uart_init);
module_exit(uart_exit);

上一节 | 目录索引 | 下一节

blog comments powered by Disqus