全部博文(298)
分类: LINUX
2011-03-24 12:52:41
(7)设备访问控制之独享设备
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
2.程序清单
本次实验程序为国嵌实验和tekkamanninja博客《LDD3》代码,本人作了改动和较为详细的注释,如有错误请指出。
gong.h
#ifndef _GONG_H_
#define _GONG_H_
#include
#ifndef GONG_MAJOR
#define GONG_MAJOR 0 /*预设的mem的主设备号*/
#endif
#ifndef GONG_NR_DEVS
#define GONG_NR_DEVS 3/*设备数*/
#endif
#ifndef GONG_SIZE
#define GONG_SIZE 4096
#endif
/*mem设备描述的结构体*/
struct gong_dev
{
char *data;
unsigned long size;
wait_queue_head_t inq;
};//注意这里要加分号!!!!!!!!!
/*定义幻数*/
#define GONG_IOC_MAGIC 'g'
/* 定义命令 ,这里的命令都是unsigned int类型*/
#define GONG_IOCPRINT _IO(GONG_IOC_MAGIC, 1) /*没有参数的命令*/
#define GONG_IOCGETDATA _IOR(GONG_IOC_MAGIC, 2, int) /*从驱动中读数据*/
#define GONG_IOCSETDATA _IOW(GONG_IOC_MAGIC, 3, int) /*写数据到驱动中*/
//定义命令的最大序列号
#define GONG_IOC_MAXNR 3
#endif
gong.c
/*********************************************
*memdev.c 阻塞版本
*********************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gong.h"
static int gong_major = GONG_MAJOR;
bool have_data = false ;/*表明设备有足够空间可供读*/
module_param(gong_major, int, S_IRUGO);
struct gong_dev *gong_devp;/*设备结构体指针*/
struct cdev cdev;
DECLARE_COMPLETION(comp);/* 创建completion(声明+初始化) */
//构造独享设备
static atomic_t gong_s_available = ATOMIC_INIT(1);//初始化为1表示能用
/*
struct file 常见成员:
loff_t f_pos 文件读写位置
struct file_operation *f_op
struct inode 常见成员:
dev_t i_rdev 设备号
*/
/*
为啥不能放在这里
static const struct file_operations gong_fops =
{
.owner = THIS_MODULE,
.llseek = gong_llseek,
.read = gong_read,
.write = gong_write,
.open = gong_open,
.release = gong_release,
.ioctl = gong_ioctl,//!!为什么这里有‘,’??
};//!!!
*/
/*文件打开函数*/
int gong_open(struct inode *inode, struct file *filp)
{
struct gong_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
//独享设备实现
if(!atomic_dec_and_test(&gong_s_available))
{
atomic_inc(&gong_s_available) ;
return -EBUSY;
}
/*大于最大的次设备号就错误了 因为注册的时候要指定次设备的数目*/
if(num >= GONG_NR_DEVS)
return -ENODEV;
dev = &gong_devp[num];//
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;//!!
return 0;
}
/*文件释放函数*/
int gong_release(struct inode *inode, struct file *filp)
{
//独享设备实现
atomic_inc(&gong_s_available) ;
return 0;
}
/*读函数*/
static ssize_t gong_read(struct file *filp, char __user *buf, size_t size, loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针由于read没有struct inode *inode结构 而且函数的
参数不能改 只能改函数名所以只能在open函数里面设置*/
struct gong_dev *dev = filp->private_data;
//并发处理
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);/
wait_for_completion(&comp);/* 等待completion */
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
/*判断读位置是否有效从内核空间读到用户空间*/
if(p >= GONG_SIZE)
return 0;
if(count > GONG_SIZE-p )
count = GONG_SIZE-p ;
/*没有数据可读 考虑为什么不用if 而用while !!!!??*/
while(!have_data)
{
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
/*wait_event_interruptible(queue, condition) 条件为真时立即返回否则让
进程进入TASK_INTERRUPTIBLE的睡眠
并挂在queue参数指定的等待队列*/
wait_event_interruptible(dev->inq, have_data);
}
/*读数据到用户空间*/
if(copy_to_user(buf, (void *)(dev->data+p), count))
ret = -EFAULT;
else
{
*poss += count;
ret = count;
printk("<5> read %d butes from %lu\n", count, p);
}
have_data = false;/*表明不再有数据可读 */
return ret;
}
/*写函数*/
static ssize_t gong_write(struct file *filp, const char __user *buf, size_t size, loff_t *poss)
{
unsigned long p = *poss;
unsigned int count = size;
int ret = 0;
struct gong_dev *dev = filp->private_data;
if(p >= GONG_SIZE)
return 0;
if(count > GONG_SIZE-p)
count = GONG_SIZE-p;
if(copy_from_user(dev->data+p, buf, count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk("<5> write %d bytes foem %lu\n", count, p);
}
//并发处理
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
complete(&comp);/*唤醒一个等待completion的线程*/
have_data = true;/*有新数据可读*/
/*唤醒读进程
wake_up(wait_queue_t *q)
从等待队列q中唤醒状态为
TASK_INTERRUPTIBLE,TASK_UNINTERRUPTIBLE,TASK_KILLABLE
的所有进程*/
wake_up(&(dev->inq));
return ret;
}
int gong_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err =0;
int ret = 0;
int ioarg = 0;
/* 检测命令的有效性 */
if(_IOC_TYPE(cmd) != GONG_IOC_MAGIC)//命令是否操作这一类设备
return -EINVAL;
if(_IOC_NR(cmd) > GONG_IOC_MAXNR)// 命令序列号是否超出定义
return -EINVAL;
/* 根据命令类型,检测参数空间是否可以访问 */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* 根据命令,执行相应的操作 */
switch(cmd) {
/* 打印当前设备信息 */
case GONG_IOCPRINT:
printk("<--- CMD GONG_IOCPRINT Done--->\n\n");
break;
/* 获取参数 */
case GONG_IOCGETDATA:
ioarg = 1101;
ret = __put_user(ioarg, (int *)arg);
break;
/* 设置参数 */
case GONG_IOCSETDATA:
ret = __get_user(ioarg, (int *)arg);
printk("<--- In Kernel GONG_IOCSETDATA ioarg = %d --->\n\n",ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
static loff_t gong_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = GONG_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>GONG_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations gong_fops =
{
.owner = THIS_MODULE,
.llseek = gong_llseek,
.read = gong_read,
.write = gong_write,
.open = gong_open,
.release = gong_release,
.ioctl = gong_ioctl,//!!为什么这里有‘,’??
};//!!!
/*设备驱动模块加载函数*/
static int gong_init(void)
{
int res;
int i;
/*dev_t描述是设备 MKDEV把主设备号和次设备号合成*/
dev_t devno = MKDEV(gong_major, 0);
/*静态申请设备号*/
/*int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:注册从from开始的count个这备号,主设备号不变,次设备号增加,如果次设备号溢出,主设备号加1
参数:
from:要注册的第一个设备号
count:要注册的设备号个数
name:设备名(体现在/proc/devices)
*/
if(gong_major)
res = register_chrdev_region(devno, GONG_NR_DEVS, "gong_dev");
else/*动态申请设备号*/
/*int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
功能:动态申请count个设备号,第一个设备号的次设备号为baseminor
参数:
dev:分配到得设备号
baseminor:起始次设备号
count:要注册的设备号个数
name:设备名(体现在/proc/devices)
*/
{
res = alloc_chrdev_region(&devno, 0, GONG_NR_DEVS, "gong_dev");
gong_major = MAJOR(devno);
}
if(res < 0)
return res;
/*设备注册:
1.分配cdev
动态分配:
struct cdev *cdev_alloc(void)
2.初始化cdev
void cdev_init(struct cdev *cdev, const struct file_operation *fops)
3.添加cdev
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
*/
/*初始化cdev结构*/
cdev_init(&cdev, &gong_fops);
//cdev.ower = THIS_MODULE; 没有这个成员
cdev.ops = &gong_fops;
/*注册字符设备*/
cdev_add(&cdev, MKDEV(gong_major, 0), GONG_NR_DEVS);
/*为设备描述结构分配内存有多少个设备分配多少个 */
gong_devp = kmalloc(GONG_NR_DEVS * sizeof(struct gong_dev), GFP_KERNEL);
if(!gong_devp)/*申请失败*/
{
res = -ENOMEM;
goto fail_malloc;
}
memset(gong_devp, 0, GONG_NR_DEVS * sizeof(struct gong_dev));
/*为设备分配内存有多少个设备分配多少个*/
for(i = 0; i < GONG_NR_DEVS; i++)
{
gong_devp[i].size = GONG_SIZE;
gong_devp[i].data = kmalloc(GONG_SIZE, GFP_KERNEL);
/*初始化等待队列*/
init_waitqueue_head(&(gong_devp[i].inq));
}
return 0;
fail_malloc:
/*void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放从from开始的count个设备号*/
unregister_chrdev_region(devno, GONG_NR_DEVS);
return res;
}
static void gong_exit(void)
{
cdev_del(&cdev);
kfree(gong_devp);
unregister_chrdev_region(MKDEV(gong_major, 0), GONG_NR_DEVS);
}
MODULE_LICENSE("GPL");
module_init(gong_init);
module_exit(gong_exit);
Arm平台实验:
[root@FriendlyARM /udisk]# insmod gong.ko
[root@FriendlyARM /udisk]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
253 gong_dev
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[root@FriendlyARM /udisk]# mknod /dev/gong_dev0 c 253 0
[root@FriendlyARM /udisk]# ./gong_read & 独占资源
[root@FriendlyARM /udisk]# buf:This is read
[root@FriendlyARM /udisk]# echo 3231 > /dev/gong_dev0
-/bin/sh: can't create /dev/gong_dev0: Device or resource busy
[root@FriendlyARM /udisk]# ./gong_write
buf:This is write
open memdev failed!