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);
}