/*======================================================================
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);
這種封裝的方法還不是很好,有機會的話繼續改進一下。