/*======================================================================
A mem_pool driver as an example of char device drivers
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "mem_pool.h"
static mem_pool_major = mem_pool_MAJOR;
/*设备结构体指针*/
struct mem_pool_dev *mem_pool_devp;
/*文件打开函数*/
int mem_pool_open(struct inode *inode, struct file *filp)
{
struct mem_pool_dev *dev;
dev = container_of(inode->i_cdev,struct mem_pool_dev,cdev);
/*将设备结构体指针赋值给文件私有数据指针*/
filp->private_data = dev;
printk("mem_pool_open func \n");
printk("dev->sem.count = %d \n",dev->sem.count);
return 0;
}
/*文件释放函数*/
int mem_pool_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctl设备控制函数 */
static int mem_pool_ioctl(struct inode *inodep, struct file *filp, unsigned
int cmd, unsigned long arg)
{
/*获得设备结构体指针*/
struct mem_pool_dev *dev = filp->private_data;
switch (cmd)
{
case MEM_CLEAR:
/* 增加并发机制 由信号量控制*/
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
memset(dev->mem, 0, mem_pool_SIZE);
printk( "mem_pool is set to zero\n");
/* 释放信号量 */
up(&dev->sem);
break;
default:
return - EINVAL;
}
return 0;
}
/*读函数*/
static ssize_t mem_pool_read(struct file *filp, char __user *buf, size_t size,
loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
/*1. 获得设备结构体指针*/
struct mem_pool_dev *dev = filp->private_data;
/*2. 分析和获取有效的写长度*/
if (p >= mem_pool_SIZE)
return count ? - ENXIO: 0;
if (count > mem_pool_SIZE - p)
count = mem_pool_SIZE - p;
/* 增加并发机制 由信号量控制*/
printk("read dev->sem = %d \n",dev->sem.count);
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
/*3. 内核空间->用户空间 */
if (copy_to_user(buf, (void*)(dev->mem + p), count))
{
ret = - EFAULT;
}
else
{
*ppos += count;
ret = count;
printk( "read %d bytes(s) from %d\n", count, p);
}
/* 释放信号量 */
up(&dev->sem);
return ret;
}
/*写函数*/
static ssize_t mem_pool_write(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
/*1. 获得设备结构体指针*/
struct mem_pool_dev *dev = filp->private_data;
/*2. 分析和获取有效的写长度*/
if (p >= mem_pool_SIZE)
return count ? - ENXIO: 0;
if (count > mem_pool_SIZE - p)
count = mem_pool_SIZE - p;
/* 增加并发机制 由信号量控制*/
printk("write dev->sem = %d \n",dev->sem.count);
/* 正常情况下(获取到信号量,sem > 0),down_interruptible返回0 */
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
/*3. 用户空间->内核空间*/
printk("copy_from_user \n");
printk("dev->mem + p = %x \n",dev->mem + p);
if (copy_from_user(dev->mem + p, buf, count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk( "written %d bytes(s) from %d\n", count, p);
}
printk("dev->sem = %d \n",dev->sem.count);
/* 释放信号量 */
up(&dev->sem);
printk(" up(&dev->sem); \n");
printk("dev->sem = %d \n",dev->sem.count);
return ret;
}
/* seek文件定位函数 */
static loff_t mem_pool_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig)
{
case 0: /*相对文件开始位置偏移*/
if (offset < 0)
{
ret = - EINVAL;
break;
}
if ((unsigned int)offset > mem_pool_SIZE)
{
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if ((filp->f_pos + offset) > mem_pool_SIZE)
{
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
/*文件操作结构体*/
static const struct file_operations mem_pool_fops =
{
.owner = THIS_MODULE,
.llseek = mem_pool_llseek,
.read = mem_pool_read,
.write = mem_pool_write,
.ioctl = mem_pool_ioctl,
.open = mem_pool_open,
.release = mem_pool_release,
};
/*初始化并注册cdev*/
static void mem_pool_setup_cdev(struct mem_pool_dev *dev, int index)
{
int err, devno = MKDEV(mem_pool_MAJOR, index);
/*1. 初始化cdev,绑定设备和文件操作函数*/
cdev_init(&dev->cdev, &mem_pool_fops);
dev->cdev.owner = THIS_MODULE;
/*2. 为文件操作提供具体实现方法*/
dev->cdev.ops = &mem_pool_fops;
/*3. 添加该cdev至内核*/
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk( "Error %d adding LED%d", err, devno);
}
/*设备驱动模块加载函数*/
int mem_pool_init(void)
{
int result;
dev_t devno = MKDEV(mem_pool_major, 0);
printk( "mem_pool_init !\n");
/*1. 申请设备号*/
if (mem_pool_major)
result = register_chrdev_region(devno, 2, "mem_pool"); //申请两个设备号
else
{
/*2. 动态申请设备号 */
result = alloc_chrdev_region(&devno, 0, 2, "mem_pool"); //申请两个设备号
mem_pool_major = MAJOR(devno);
}
if (result < 0)
return result;
/*3. 动态申请设备结构体的内存*/
mem_pool_devp = kmalloc(2 * sizeof(struct mem_pool_dev), GFP_KERNEL);
/*4. 申请失败*/
if (!mem_pool_devp)
{
result = - ENOMEM;
goto fail_malloc;
}
/*5. 内存初始化*/
memset(mem_pool_devp, 0, 2 * sizeof(struct mem_pool_dev));
/*6. 注册初始化设备*/
mem_pool_setup_cdev(&mem_pool_devp[0] , 0);
mem_pool_setup_cdev(&mem_pool_devp[1] , 1);
/*7. 初始化信号量*/
init_MUTEX(&mem_pool_devp[0].sem);
/*8. 初始化信号量*/
// init_MUTEX(&mem_pool_devp[1].sem);
return 0;
fail_malloc: unregister_chrdev_region(devno, 2);
return result;
}
/*模块卸载函数*/
void mem_pool_exit(void)
{
printk( "mem_pool_exit !\n");
/*1. 注销cdev*/
cdev_del(&(mem_pool_devp[0].cdev));
cdev_del(&(mem_pool_devp[1].cdev));
/*2. 释放设备结构体内存*/
kfree(mem_pool_devp);
/*3. 释放设备号*/
unregister_chrdev_region(MKDEV(mem_pool_major, 0), 2);
}
MODULE_AUTHOR("xb Deng");
MODULE_LICENSE("Dual BSD/GPL");
module_param(mem_pool_major, int, S_IRUGO);
module_init(mem_pool_init);
module_exit(mem_pool_exit);
1.inode是如何来的,这个应该和mknod这个命令有关mknod /dev/mem_pool c 250 0 这个命令生成了一个mem_pool的设备节点,
然后当我们在应用程序中打开这个设备文件的时候,系统自动赋予了inode结构体的值并且在inode结构体中,保存着一些重要的信息,
如:设备号,cdev的指针地址。为什么要创建设备节点,就是为了能够创建这些相关的信息,这里不讨论,file和inode的数据结构,
在网上随便一搜,能够搜到一大把。
2.在使用printk打印调试的时候,碰到很多愚昧的调试语句,比如
printk("down_interruptible(&dev->sem) = %d \n",down_interruptible(&dev->sem));
试想一下。执行了这句之后不就相当于获取了一次信号量么,难怪运行程序的时候总是会停在那里。
3.在多个设备的时候,内存块也要申请多个,同时别忘记了,信号量的申请也需要多个,具体如果你不这么做,会产生什么结果,可以
自己试试
4. 创建两个设备节点就可以进行测试两个设备了
mknod /dev/mem_pool0 c 249 0
mknod /dev/mem_pool1 c 249 1
5.用户空间的测试程序如下所示:
#include <stdio.h>
int main()
{
FILE *fp0 = NULL;
char Buf[4096];
/*初始化Buf*/
strcpy(Buf,"mem_pool is char dev!");
printf("BUF: %s\n",Buf);
/*打开设备文件*/
fp0 = fopen("/dev/mem_pool1","r+");
if (fp0 == NULL)
{
printf("Open mem_pool Error!\n");
return -1;
}
printf("Open mem_pool Success!\n");
/*写入设备*/
fwrite(Buf, sizeof(Buf), 1, fp0);
printf("Write mem_pool Success!\n");
/*重新定位文件位置(思考没有该指令,会有何后果)*/
fseek(fp0,0,SEEK_SET);
printf("seek mem_pool Success!\n");
/*清除Buf*/
strcpy(Buf,"Buf is NULL!");
printf("BUF: %s\n",Buf);
/*读出设备*/
fread(Buf, sizeof(Buf), 1, fp0);
printf("Read mem_pool Success!\n");
/*检测结果*/
printf("BUF: %s\n",Buf);
fclose(fp0);
return 0;
}
最后可以看看这个头文件里面的内容
mem_pool.h
#define mem_pool_SIZE 0x1000 /*全局内存最大8K字节*/
#define MEM_CLEAR 0x1 /*清0全局内存*/
#define mem_pool_MAJOR 249 /*预设的mem_pool的主设备号*/
struct mem_pool_dev
{
struct cdev cdev; /*cdev结构体*/
unsigned char mem[mem_pool_SIZE]; /*全局内存*/
struct semaphore sem;
};/*mem_pool设备结构体*/
int mem_pool_open(struct inode *inode, struct file *filp);
int mem_pool_release(struct inode *inode, struct file *filp);
这种封装的方法还不是很好,有机会的话继续改进一下。