几天前一直在看Linux驱动开发,看了几天,今天才主动动手开始去抄袭代码.第一眼见这东西,我迷糊迷糊的.摸不着头脑,太可怕了..今天抄袭了一边.把Linux驱动的大概给观摩了一遍,总之也就那样吧...
第一个驱动
首先实现一个简单的字符型驱动,在内存区分配一块可读可写的内存,通过文件读写接口对这块内核中的内存进行区域操作.
模块定义:
struct DEMO_dev { struct cdev cdev; }; //设备结构 struct file_operations DEMO_fops = { .owner = THIS_MODULE, .llseek = DEMO_llseek, .read = DEMO_read, .write = DEMO_write, .ioctl = DEMO_ioctl, .release = DEMO_release, }; //初始化一个系统调用的结构体 struct DEMO_dev *DEMO_devices; static unsigned char demo_inc = 0; static u8 demoBuffer[256]; //分配的内存空间 #define DEMO_MAJOR 224 //主设备号
模块初始化:
int DEMO_init_module(void) { int result; dev_t dev = 0; //设备号 dev = MKDEV(DEMO_MAJOR,0); //转换设备号 result = register_chrdev_region(dev,1,"DEMO"); //按照指定的设备号注册设备 if( result < 0 ) { printk(KERN_WARNING "DEMO:can't get major %d/n",DEMO_MAJOR); //注册失败输出错误信息 return result; } //分配DEMO_dev,该结构是自定义的 DEMO_devices = kmalloc(sizeof(struct DEMO_dev),GFP_KERNEL); //分配设备结构内存 if(!DEMO_devices) { result = -ENOMEM; goto fail; } memset( DEMO_devices,0,sizeof( struct DEMO_dev ) ); //初始化一个字符驱动 cdev_init(&DEMO_devices->cdev,&DEMO_fops); //cdev初始化 DEMO_devices->cdev.owner = THIS_MODULE; //写入模块所属者 DEMO_devices->cdev.ops = &DEMO_fops; //模块操作接口 //向内核添加字符设备 result = cdev_add(&DEMO_devices->cdev,dev,1); if( result ) { printk(KERN_NOTICE "Error %d adding DEMO/n",result); goto fail; } return 0; fail: DEMO_cleanup_module(); //失败清除模块 return result; }
模块卸载:
void DEMO_cleanup_module(void) { dev_t devno = MKDEV( DEMO_MAJOR,0 ); //删除键盘 if( DEMO_devices ) { cdev_del(&DEMO_devices->cdev); kfree(DEMO_devices); } //释放设备号 unregister_chrdev_region(devno,1); }
//模块的打开操作
int DEMO_open(struct inode *inode, struct file *filp) { struct DEMO_dev *dev; //防止多次打开设备 if( demo_inc > 0 )return -ERESTARTSYS; demo_inc++; //获取DEMO_dev结构,并将它赋给filp->private_data,以后可以用filp->private_data访问DEMO_dev结构 dev = container_of(inode->i_cdev,struct DEMO_dev,cdev); //container_of宏 filp->private_data = dev; return 0; }
//模块的释放操作
int DEMO_release(struct inode *inode, struct file *filp) { //引用技术减少 demo_inc--; return 0; }
//模块的读操作
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int result; //得到文件的当前地址 loff_t pos = *f_pos; //判断位置合法性 if(pos >= 256) { result = 0; goto out; } if( count > ( 256 - pos ) ) { count = 256 - pos; } pos += count; if( copy_to_user(buf,demoBuffer + *f_pos,count) ) { count = -EFAULT; goto out; } //改变文件的当前位置 *f_pos = pos; out: return count; }
//模块的写操作
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = -ENOMEM; loff_t pos = *f_pos; //判断位置的合法性 if( pos >= 256 ) { goto out; } if( count > ( 256 - pos ) ) { count = 256 - pos; } pos += count; //将数据复制到内核空间 if( copy_from_user(demoBuffer + *f_pos,buf,count) ) { retval = -EFAULT; goto out; } //改变文件的当前位置 *f_pos = pos; return count; out: return retval; }
//模块的调整操作
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence) { loff_t pos; pos = filp->f_pos; switch( whence ) { case 0: pos = off; break; case 1: pos += off; break; case 2: default: return -EINVAL; } //超出范围 if( (pos > 256) || (pos < 0) ) { return -EINVAL; } return filp->f_pos = pos; }
//模块的控制
int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { if( cmd == 0 ) { printk("ioctl command1 successfully/n"); return 0; } if( cmd == 1 ) { printk("ioctl command2 successfully/n"); return 0; } printk("ioctl error/n"); return -EFAULT; }
抱歉.我还不是很会写Makefile所以.我就用很见的方法来编译的.下面给出我的Makefile
obj-m := Memory.o
编译驱动是需要源代码的.我用的是ubLinux.
make -C /forlinx/Linux-2.6.28.6/ M$=(pwd) modules
给出测试代码跟驱动代码的完整版:
驱动代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
MODULE_AUTHOR("fgj");
MODULE_LICENSE("Dual BSD/GPL");
int DEMO_init_module(void);
void DEMO_cleanup_module(void);
int DEMO_release(struct inode *inode, struct file *filp);
int DEMO_open(struct inode *inode, struct file *filp);
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence);
int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
struct DEMO_dev
{
struct cdev cdev;
};
struct file_operations DEMO_fops = {
.owner = THIS_MODULE,
.llseek = DEMO_llseek,
.read = DEMO_read,
.write = DEMO_write,
.ioctl = DEMO_ioctl,
.release = DEMO_release,
};
struct DEMO_dev *DEMO_devices;
static unsigned char demo_inc = 0;
static u8 demoBuffer[256]; //分配的内存空间
#define DEMO_MAJOR 224 //主设备号
int DEMO_init_module(void)
{
int result;
dev_t dev = 0;
dev = MKDEV(DEMO_MAJOR,0);
result = register_chrdev_region(dev,1,"DEMO");
if( result < 0 )
{
printk(KERN_WARNING "DEMO:can't get major %d/n",DEMO_MAJOR);
return result;
}
//分配DEMO_dev,该结构是自定义的
DEMO_devices = kmalloc(sizeof(struct DEMO_dev),GFP_KERNEL);
if(!DEMO_devices)
{
result = -ENOMEM;
goto fail;
}
memset( DEMO_devices,0,sizeof( struct DEMO_dev ) );
//初始化一个字符驱动
cdev_init(&DEMO_devices->cdev,&DEMO_fops);
DEMO_devices->cdev.owner = THIS_MODULE;
DEMO_devices->cdev.ops = &DEMO_fops;
//向内核添加字符设备
result = cdev_add(&DEMO_devices->cdev,dev,1);
if( result )
{
printk(KERN_NOTICE "Error %d adding DEMO/n",result);
goto fail;
}
return 0;
fail:
DEMO_cleanup_module();
return result;
}
void DEMO_cleanup_module(void)
{
dev_t devno = MKDEV( DEMO_MAJOR,0 );
//删除键盘
if( DEMO_devices )
{
cdev_del(&DEMO_devices->cdev);
kfree(DEMO_devices);
}
//释放设备号
unregister_chrdev_region(devno,1);
}
module_init(DEMO_init_module);
module_exit(DEMO_cleanup_module);
int DEMO_open(struct inode *inode, struct file *filp)
{
struct DEMO_dev *dev;
//防止多次打开设备
if( demo_inc > 0 )return -ERESTARTSYS;
demo_inc++;
//获取DEMO_dev结构,并将它赋给filp->private_data,以后可以用filp->private_data访问DEMO_dev结构
dev = container_of(inode->i_cdev,struct DEMO_dev,cdev); //container_of宏
filp->private_data = dev;
return 0;
}
int DEMO_release(struct inode *inode, struct file *filp)
{
//引用技术减少
demo_inc--;
return 0;
}
ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int result;
//得到文件的当前地址
loff_t pos = *f_pos;
//判断位置合法性
if(pos >= 256)
{
result = 0;
goto out;
}
if( count > ( 256 - pos ) )
{
count = 256 - pos;
}
pos += count;
if( copy_to_user(buf,demoBuffer + *f_pos,count) )
{
count = -EFAULT;
goto out;
}
//改变文件的当前位置
*f_pos = pos;
out:
return count;
}
ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
ssize_t retval = -ENOMEM;
loff_t pos = *f_pos;
//判断位置的合法性
if( pos >= 256 )
{
goto out;
}
if( count > ( 256 - pos ) )
{
count = 256 - pos;
}
pos += count;
//将数据复制到内核空间
if( copy_from_user(demoBuffer + *f_pos,buf,count) )
{
retval = -EFAULT;
goto out;
}
//改变文件的当前位置
*f_pos = pos;
return count;
out:
return retval;
}
loff_t DEMO_llseek(struct file *filp, loff_t off, int whence)
{
loff_t pos;
pos = filp->f_pos;
switch( whence )
{
case 0:
pos = off;
break;
case 1:
pos += off;
break;
case 2:
default:
return -EINVAL;
}
//超出范围
if( (pos > 256) || (pos < 0) )
{
return -EINVAL;
}
return filp->f_pos = pos;
}
int DEMO_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
if( cmd == 0 )
{
printk("ioctl command1 successfully/n");
return 0;
}
if( cmd == 1 )
{
printk("ioctl command2 successfully/n");
return 0;
}
printk("ioctl error/n");
return -EFAULT;
}
//测试代码
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>
#define COMMAND1 0
#define COMMAND2 1
void main()
{
int fd;
int i;
char data[256];
int retval;
fd = open("/dev/fgj",O_RDWR);
if(fd==-1)
{
perror("error open/n");
exit(-1);
}
printf("open /dev/smbus successfully/n");
retval=ioctl(fd,COMMAND1,0);
if(retval==-1)
{
perror("ioctl error/n");
exit(-1);
}
printf("send command1 successfully/n");
retval=write(fd,"LvApp",5);
if(retval==-1)
{
perror("write error/n");
exit(-1);
}
retval=lseek(fd,0,0);
if(retval==-1)
{
perror("lseek error/n");
exit(-1);
}
retval=read(fd,data,5);
if(retval==-1)
{
perror("read error/n");
exit(-1);
}
printf("read successfully:%s/n",data);
close(fd);
}