Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2414681
  • 博文数量: 298
  • 博客积分: 7876
  • 博客等级: 准将
  • 技术积分: 5500
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-23 13:39
文章存档

2013年(2)

2012年(142)

2011年(154)

分类: 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 /*completion*/

#include /*printk()*/

#include /*kmalloc*/

#include /*everything....*/

#include /*error codes*/

#include /*size_t*/

#include /*O_ACCMODE*/

#include

#include

#include /*capable()*/

 

#include

#include

#include /*access_ok()*/

 

#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!

 

阅读(897) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~