utrs_driver.h
----------------------------------------------------------------------------------------------------------------
#include<asm/hardware.h>
#define UART_ULCON1 __REG(0x50004000 + 0x00)
#define UART_UCON1 __REG(0x50004000 + 0x04)
#define UART_URXH1 __REG(0x50004000 + 0x24)
#define UART_UTXH1 __REG(0x50004000 + 0x20)
#define UART_UBRDIV1 __REG(0x50004000 + 0x28)
#define UART_UTRSTAT1 __REG(0x50004000 + 0x10)
----------------------------------------------------------------------------------------------------------------
utrs_driver.c
----------------------------------------------------------------------------------------------------------------
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/hardware.h>
#include "utrs_driver.h"
#define DEVICE_NAME "akem" //设备文件名
#define BUTTON_MAJOR 232 //主设备号
/* 打开驱动inode结构在内部表示文件,file结构是打开上面传来的文件描述符fd对应的file结构,file结构都指向单个inode */
static int akem_open(struct inode *inode, struct file * file)
{
/* 设置寄存器 */
UART_UCON1 = 0x05;
UART_ULCON1 = 0x03;
printk("Kernel : in open,we do nothing......\n");
return 0;
}
/* 从设备读取数据,上层的read函数会调用到这里,file是read的fd对应的结构, ppos是系统回调 */
static int akem_read(struct file * file, char * buff, size_t count, loff_t *ppos)
{
int i;
unsigned char ch;
for(i = 0; i < count; i++)
{
while(!(UART_UTRSTAT1 & 0x1)); /* 扫描寄存器是否有数据 */
ch = UART_URXH1; /* 从寄存器里读出一个字节 */
*(buff + i) = ch; /* 存放在buff里 */
}
return strlen(buff); /* 返回buff的长度 */
}
/* 向设备写入数据,上层的write函数会调用到这里,file是write的fd对应的结构, offp是系统回调 */
int akem_write( struct file *filp, const char *buff, size_t count, loff_t *offp )
{
int i;
unsigned char ch;
for(i = 0; i < count; i++)
{
ch = *(buff + i);
while(!((UART_UTRSTAT1 & 0x2) == 0x2)); /* 扫描寄存器是否有数据 */
UART_UTXH1 = ch; /* 把一格字节写入寄存器 */
}
return 0;
}
/* 设置串口的波特率 */
static int akem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
ret = -EINVAL;
switch(cmd)
{
case 111: /* 功过命令来配置波特率 */
{
if(arg == 115200)
{
UART_UBRDIV1 = 26;
ret = 0;
}
}; break;
default:
return -EINVAL;
}
printk("Kernel : in ioctl, (%d, %d)\n", cmd, arg);
}
int akem_release(struct inode *inode, struct file *filp)
{
printk("release!!\n");
return 0;
}
/* file_operations结构是建立驱动程序和设备编号的连接,内部是一组函数指针,每个打开的文件,也就是file结构,和一组函数关联,这些操作主要用来实现系统调用的 */
static struct file_operations akae_fops = {
owner: THIS_MODULE,
open: akae_open,
ioctl: akae_ioctl,
read: akae_read,
write: akae_write,
release: akae_release
};
/* 初始化驱动模块,驱动程序要注册一个设备文件,并对其进行操作 */
static int __init akem_init(void)
{
int ret;
int ready = 0;
/* 注册一个字符设备,并分配设备编号,major是设备号,name是驱动程序名称 fops是file_operations结构 */
ret = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &akae_fops);
if (ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
printk(" init ok!......\n");
return 0; /* 注册成功返回0 */
}
/* 卸载模块 */
static void __exit akem_exit(void)
{
/* 如果不使用该设备时释放编号 */
unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);
printk("bye !\n");
}
module_init(akem_init);
module_exit(akem_exit);
MODULE_LICENSE("GPL");
----------------------------------------------------------------------------------------------------------------
test.c 测试模块
这个测试模快是以应用程序调用驱动,而不以模块的形式加载,所以这正是驱动本质
----------------------------------------------------------------------------------------------------------------
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define BOUNDRATE 111 /* 设置伯特率命令参数 */
int main(void)
{
int fd;
int ret = 0;
char buf[100] = "HELLO FUCK!\n\r";
char ch;
fd = open("/temp/akem", O_RDWR); /* open会直接调用到驱动里的akem_open */
if(fd == -1)
{
perror("open");
exit(1);
}
ret = ioctl(fd, BOUNDRATE, 115200); /* 设置伯特律115200 */
ret = write(fd, buf, strlen(buf)); /* write会调用驱动里的akem_write */
if(ret == -1)
{
perror("write error");
exit(1);
}
while(ch != '#')
{
read(fd, &ch,1);
printf("%c",ch);
fflush(stdout);
}
close(fd);
}