/*======================================================================
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);
这种封装的方法还不是很好,有机会的话继续改进一下。
分享到:
相关推荐
信号量和互斥体 completion 自旋锁 锁陷阱 除了锁之外的办法 快速参考 第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 第七章 时间、延迟...
信号量和互斥体 completiOn 自旋锁 锁陷阱 除了锁之外的办法 快速参考 ch06.第六章 高级字符驱动程序操作 ioctl 阻塞型I/O poll和select 异步通知 定位设备 设备文件的访问控制 快速参考 ch07.第七章 时间、延迟及...
6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受...
第二篇 勿于浮砂筑高台——Linux驱动基础篇 第3章Linux内核综述 3.1 OS基本概念 3.1.1多用户系统 3.1.2用户和组 3.1.3进程 3.1.4 Linux单核架构 3.2 Linux内核综述 3.2.1进程/内核模型综述 3.2.2内存管理综述 3.2.3...
《linux内核源代码情景分析》(非扫描电子版本) 第1章 预备知识 1.1 Linux内核简介 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的...
6.8 信号量 第7章 基于socket的进程间通信 7.1 系统调用socket() 7.2 函数sys—socket()——创建插口 7.3 函数sys—bind()——指定插口地址 7.4 函数sys—listen()——设定server插口 7.5 函数sys—...
接着系统地讲解了嵌入式Linux的环境搭建,以及嵌入式Linux的I/O与文件系统的开发、进程控制开发、进程间通信开发、网络应用开发、基于中断的开发、设备驱动程序的开发以及嵌入式图形界面的开发等,并且还安排了丰富...
-国嵌应用班-5-2(信号量).avi -国嵌应用班-5-3(必修实验).avi -第6天(进程间通讯) -国嵌应用班-6-1(线程基础).avi -国嵌应用班-6-2(多线程程序设计).avi -国嵌应用班-6-3(必修实验).avi -第7天(网络...
信号量和互斥体 111 completion 116 自旋锁 118 锁陷阱 123 除了锁之外的办法 125 快速参考 132 第六章 高级字符驱动程序操作 137 ioctl 137 阻塞型I/O 149 poll和select 163 异步通知 168 定位设备 172 ...
6.8 信号量 《LINUX内核源代码情景分析(下册)》图书目录如下: -------------------------------------------------------------------------------- 第 7章 基于socket的进程间通信 7.1 系统调用socket() ...
6.8 信号量 《LINUX内核源代码情景分析(下册)》图书目录如下: -------------------------------------------------------------------------------- 第7章 基于socket的进程间通信 7.1 系统调用socket() ...
-国嵌应用班-5-2(信号量).avi -国嵌应用班-5-3(必修实验).avi -第6天(进程间通讯) -国嵌应用班-6-1(线程基础).avi -国嵌应用班-6-2(多线程程序设计).avi -国嵌应用班-6-3(必修实验).avi -第7天(网络...
14.1.3 linux的信号量机制 490 14.1.4 使用信号量 492 14.2 共享内存 496 14.2.1 shmget函数 497 14.2.2 shmat函数 497 14.2.3 shmdt 498 14.2.4 shmctl 498 14.3 消息队列 502 14.3.1 msgget函数 502 ...
14.1.3 linux的信号量机制 490 14.1.4 使用信号量 492 14.2 共享内存 496 14.2.1 shmget函数 497 14.2.2 shmat函数 497 14.2.3 shmdt 498 14.2.4 shmctl 498 14.3 消息队列 502 14.3.1 msgget函数 502 ...
1.该文件夹下所有的文件示例,都是基于宋宝华《 linux设备驱动程序开发详解,基于最新4.0内核》写的,所有的代码都经过了调试和验证并附有日志文档。2.globalfifo是在堆内存里面通过kzalloc()函数申请的一段内存,...
4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议族简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考...
信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程...
在Linux系统下,使用与文件相关的系统调用实现对物理设备文件的读写,参照Linux系统源代码以及Grub系统的源代码,对不同介质上的FAT格式文件系统进行分析。要求在Linux环境下设计出C语言程序,实现以下功能: 1)...