Chinaunix首页 | 论坛 | 博客
  • 博客访问: 380053
  • 博文数量: 57
  • 博客积分: 2299
  • 博客等级: 大尉
  • 技术积分: 1109
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-27 23:12
文章分类
文章存档

2011年(4)

2010年(53)

分类: 嵌入式

2010-02-28 00:42:39

Linux设备驱动程序学习[5]—高级字符驱动程序操作2
一、   休眠
进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。这个进程将不被在任何 CPU 上调度,即将不会运行。 直到发生某些事情改变了那个状态。安全地进入休眠的两条
规则:
(1) 永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者 RCU 锁时不能睡眠;关闭中断也不能睡眠。持有一个信号量时休眠是合法的,但你应当仔细查看代码:如果代码在持有一个信号量时睡眠,任何其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂,而且决不能阻塞那个将最终唤醒你的进程。
(2)当进程被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在它之前醒来并获取了所等待的资源。所以不能对唤醒后的系统状态做任何的假设,并必须重新检查等待条件来确保正确的响应。
除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个称为等待队列的数据结构。它是一个进程链表,其中饱含了等待某个特定事件的所有进程。在 Linux 中, 一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中。
wait_queue_head_t 类型的数据结构非常简单:
struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
它包含一个自旋锁和一个链表。这个链表是一个等待队列入口,它被声明做 wait_queue_t。wait_queue_head_t包含关于睡眠进程的信息和它想怎样被唤醒。
自旋锁 现在还不理解什么意思,上一章没有看。
简单休眠(其实是高级休眠的宏)
Linux 内核中最简单的休眠方式是称为 wait_event的宏(及其变种),它实现了休眠和进程等待的条件的
检查。形式如下:
wait_event(queue, condition)/*不可中断休眠,不推荐*/
wait_event_interruptible(queue, condition)/*推荐,返回非零值意味着休眠被中断,且驱动应返回 -ERESTARTSYS*/
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
/*有限的时间的休眠;若超时,则不管条件为何值返回0,*/
唤醒休眠进程的函数称为 wake_up,形式如下:
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
惯例:     用 wake_up 唤醒 wait_event ;
用 wake_up_interruptible 唤醒wait_event_interruptible
上述读书笔记为前辈整理,懒得整理借来用了,谢谢前辈!
实验例程则按照自己阅读《LDD3》写成,由于休眠这部分没有涉及scull设备的内存操作,故抛弃了scull设备,重新建立sleepy设备,进程的休眠与唤醒通过标志位flag控制;
Read时:(flag != 0)为假 –> 休眠;(flag != 0)为真 –> 唤醒;
从上述可以看出wait_event_interruptible(queue, condition)宏的使用,在condition这个布尔表达式为真之前进程保持休眠;即condition为假->休眠,condition为真->唤醒;
Write时:flag = 1;即可唤醒读的进程
实验程序:

/*
*File Name :sleepy.c
*Function :test character device driver, only register one scull
*Author :gufeiyang
*From :<>
*Time :2010-2-12 home yunnan
*/

#include <linux/module.h> /*EXPORT_SYMBOL_GPL ())*/
#include <linux/moduleparam.h> /*module_param(variable, type, perm); */
#include <linux/init.h> /*module_init(init_function); module_exit(cleanup_function);*/
#include <linux/kernel.h> /*int printk(const char * fmt, ...);*/
#include <linux/types.h> /* size_t */
#include <linux/kdev_t.h> /*dev_t*/
#include <linux/fs.h> /* everything... */
#include <linux/cdev.h> /*struct cdev *cdev_alloc(void); */
#include <linux/device.h> /*struct class*/
#include <linux/sched.h> /* current and everything */
#include <linux/wait.h>
#define DEV_NAME "sleepy"
#define SLEEP_MAJOR 0
static int sleepy_major = SLEEP_MAJOR;
static int sleepy_minor = 0;
static struct cdev *sl_cdev;
static struct class *scull_class;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
static ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,loff_t *pos)
{
       printk(KERN_WARNING "process %i (%s) awakening the readers...\n",
                 current->pid, current->comm);
       flag = 1;
       wake_up_interruptible(&wq);
       return count; /* succeed, to avoid retrial */
}
static ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
       printk(KERN_WARNING "process %i (%s) going to sleep\n",
                 current->pid, current->comm);
       wait_event_interruptible(wq, flag != 0);
       flag = 0;
       printk(KERN_WARNING "awoken %i (%s)\n", current->pid, current->comm);
       return 0; /* EOF */
}
static struct file_operations sleepy_fops = {
       .owner = THIS_MODULE,
       .read = sleepy_read,
       .write = sleepy_write
// .open = scull_open,

// .release = scull_release

};
static int __init sleepy_init(void)
{
       int result,err;
       dev_t dev;
/*
       * Get a range of minor numbers to work with, asking for a dynamic
       * major unless directed otherwise at load time.
 */

       if (sleepy_major) {
              dev = MKDEV(sleepy_major, sleepy_minor);
              result = register_chrdev_region(dev, 1, DEV_NAME);
       } else {
              result = alloc_chrdev_region(&dev, sleepy_minor, 1, DEV_NAME);
              sleepy_major = MAJOR(dev);
       }
       if (result < 0) {
              printk(KERN_WARNING "scull: can't get major %d\n", sleepy_major);
              return result;
       }else {
              printk(KERN_WARNING"Scull_major = %d\n", sleepy_major);
       }
       
       sl_cdev = cdev_alloc();
// sl_cdev->ops = &sleepy_fops;

       cdev_init(sl_cdev, &sleepy_fops);
       sl_cdev->owner = THIS_MODULE;
       err = cdev_add(sl_cdev, dev, 1) ;
       if (err)
       {
              printk(KERN_NOTICE "Error %d adding sleepy_cdev", err);
       }
              
       scull_class = class_create(THIS_MODULE, DEV_NAME);
       if(IS_ERR(scull_class))
       {
              printk(KERN_ALERT"Err:faile in scull_class!\n");
              return -1;
       }
       /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/
       class_device_create(scull_class, NULL, dev, NULL, DEV_NAME);
       printk(KERN_WARNING"Module Initialed!\nChracter Device Driver Start!\n");
       return 0;
}
static void __exit sleey_exit(void)
{
       int devno = MKDEV(sleepy_major, sleepy_minor );
       
       unregister_chrdev_region(devno, 1);
       cdev_del(sl_cdev);
       
       class_device_destroy(scull_class, devno);
       class_destroy(scull_class);
       printk(KERN_WARNING"Module exit!\nChracter Device Driver End!\n");
}
module_init(sleepy_init);
module_exit(sleey_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CHARACTER DEVICE DRVER");
MODULE_AUTHOR("gufeiyang@2010-02-12");
/*
*File Name :sleepy.c
*Function :test character device driver, only register one scull
*Author :gufeiyang
*From :<>
*Time :2010-2-12 home yunnan */

#include <linux/module.h> /*EXPORT_SYMBOL_GPL ())*/
#include <linux/moduleparam.h> /*module_param(variable, type, perm); */
#include <linux/init.h> /*module_init(init_function); odule_exit(cleanup_function); */
#include <linux/kernel.h> /*int printk(const char * fmt, ...);*/
#include <linux/types.h> /* size_t */
#include <linux/kdev_t.h> /*dev_t*/
#include <linux/fs.h> /* everything... */
#include <linux/cdev.h> /*struct cdev *cdev_alloc(void); */
#include <linux/device.h> /*struct class*/
#include <linux/sched.h> /* current and everything */
#include <linux/wait.h>
#define DEV_NAME "sleepy"
#define SLEEP_MAJOR 0
static int sleepy_major = SLEEP_MAJOR;
static int sleepy_minor = 0;
static struct cdev *sl_cdev;
static struct class *scull_class;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
static ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,loff_t *pos)
{
       printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
                 current->pid, current->comm);
       flag = 1;
       wake_up_interruptible(&wq);
       return count; /* succeed, to avoid retrial */
}
static ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
       printk(KERN_DEBUG "process %i (%s) going to sleep\n",
                 current->pid, current->comm);
       wait_event_interruptible(wq, flag != 0);
       flag = 0;
       printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
       return 0; /* EOF */
}
static struct file_operations sleepy_fops = {
       .owner = THIS_MODULE,
       .read = sleepy_read,
       .write = sleepy_write
// .open = scull_open,

// .release = scull_release

};
static int __init sleepy_init(void)
{
       int result,err;
       dev_t dev;
/*
       * Get a range of minor numbers to work with, asking for a dynamic
       * major unless directed otherwise at load time.
 */

       if (sleepy_major) {
              dev = MKDEV(sleepy_major, sleepy_minor);
              result = register_chrdev_region(dev, 1, DEV_NAME);
       } else {
              result = alloc_chrdev_region(&dev, sleepy_minor, 1, DEV_NAME);
              sleepy_major = MAJOR(dev);
       }
       if (result < 0) {
              printk(KERN_WARNING "scull: can't get major %d\n", sleepy_major);
              return result;
       }else {
              printk(KERN_WARNING"Scull_major = %d\n", sleepy_major);
       }
       
       sl_cdev = cdev_alloc();
// sl_cdev->ops = &sleepy_fops;

       cdev_init(sl_cdev, &sleepy_fops);
       sl_cdev->owner = THIS_MODULE;
       err = cdev_add(sl_cdev, dev, 1) ;
       if (err)
       {
              printk(KERN_NOTICE "Error %d adding sleepy_cdev", err);
       }
              
       scull_class = class_create(THIS_MODULE, DEV_NAME);
       if(IS_ERR(scull_class))
       {
              printk(KERN_ALERT"Err:faile in scull_class!\n");
              return -1;
       }
       /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/
       class_device_create(scull_class, NULL, dev, NULL, DEV_NAME);
//PS:一时大意将DEV写成主设备号了,生成的设备节点与实际不符,故出现应用程序打开设备错误!

       printk(KERN_WARNING"Module Initialed!\nChracter Device Driver Start!\n");
       return 0;
}
static void __exit sleey_exit(void)
{
       int devno = MKDEV(sleepy_major, sleepy_minor );
       
       unregister_chrdev_region(devno, 1);
       cdev_del(sl_cdev);
       
       class_device_destroy(scull_class, devno);
       class_destroy(scull_class);
       printk(KERN_WARNING"Module exit!\nChracter Device Driver End!\n");
}
module_init(sleepy_init);
module_exit(sleey_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CHARACTER DEVICE DRVER");
MODULE_AUTHOR("gufeiyang@2010-02-12");
/*sleepy-test-w-r.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

#define SLEEPY_W
int main()
{
       int fd,code;
#ifdef SLEEPY_W
       if((fd = open("/dev/sleepy",O_WRONLY)) == -1)
{
       perror("Open");
       return -1;
} else {
       printf("File Opened!\n");
}
       if((code = write(fd, NULL, 0)) < 0)
{
       perror("Write");
} else {
       printf("write ok! code = %d\n", code);
}
       close(fd);
#endif
#ifdef SLEEPY_R
              if((fd = open("/dev/sleepy",O_RDONLY)) == -1)
{
       perror("Open");
       return -1;
} else {
       printf("File Opened!\n");
}
              if((code = read(fd, NULL, 0)) < 0)
{
       perror("Read");
} else {
       printf("read ok! code = %d\n", code);
}
              close(fd);
#endif
       return 0;
}

实验结果如下所示:
[root@gfy-S3C2440 /tmp]# ./sleepy-testr &
[root@gfy-S3C2440 /tmp]# File Opened!
process 930 (sleepy-testr) going to sleep
[root@gfy-S3C2440 /tmp]# ps
  PID USER       VSZ STAT COMMAND
    1 root      2072 S    init
    2 root         0 SW<  [kthreadd]
    3 root         0 SW<  [ksoftirqd/0]
    4 root         0 SW<  [watchdog/0]
    5 root         0 SW<  [events/0]
    6 root         0 SW<  [khelper]
   68 root         0 SW<  [kblockd/0]
  112 root         0 SW<  [kswapd0]
  113 root         0 SW<  [aio/0]
  753 root         0 SW<  [mtdblockd]
  754 root         0 SW<  [ftld]
  781 root         0 SW<  [kpsmoused]
  792 root         0 SW<  [rpciod/0]
  815 root      2072 S    /usr/sbin/telnetd -l /bin/login
  818 root      2076 S    -/bin/sh
  823 nobody    3364 S    boa
  930 root       700 S    ./sleepy-testr #进程已经睡着,初始flag = 0 ,condition为假
  931 root      2076 R    ps
[root@gfy-S3C2440 /tmp]# ./sleepy-testw吧
File Opened!
process 932 (sleepy-testw) awakening the readers...
awoken 930 (sleepy-testr)
read ok! code = 0
write ok! code = 0
[1] + Done                       ./sleepy-testr
 
[root@gfy-S3C2440 /tmp]# ./sleepy-testr &
[root@gfy-S3C2440 /tmp]# File Opened!
process 933 (sleepy-testr) going to sleep
 
[root@gfy-S3C2440 /tmp]# ./sleepy-testr &
[root@gfy-S3C2440 /tmp]# File Opened!
process 934 (sleepy-testr) going to sleep
 
[root@gfy-S3C2440 /tmp]# ./sleepy-testr &
[root@gfy-S3C2440 /tmp]# File Opened!
process 935 (sleepy-testr) going to sleep
 
[root@gfy-S3C2440 /tmp]# ps
  PID USER       VSZ STAT COMMAND
    1 root      2072 S    init
    2 root         0 SW<  [kthreadd]
    3 root         0 SW<  [ksoftirqd/0]
  753 root         0 SW<  [mtdblockd]
  754 root         0 SW<  [ftld]
  781 root         0 SW<  [kpsmoused]
  792 root         0 SW<  [rpciod/0]
  815 root      2072 S    /usr/sbin/telnetd -l /bin/login
  818 root      2076 S    -/bin/sh
  823 nobody    3364 S    boa
  933 root       700 S    ./sleepy-testr
  934 root       700 S    ./sleepy-testr
  935 root       700 S    ./sleepy-testr
  936 root      2076 R    ps
[root@gfy-S3C2440 /tmp]# ./sleepy-testw
File Opened!
process 937 (sleepy-testw) awakening the readers...
awoken 933 (sleepy-testr)
read ok! code = 0
write ok! code = 0
[1]   Done                       ./sleepy-testr
[root@gfy-S3C2440 /tmp]# ./sleepy-testw
File Opened!
process 938 (sleepy-testw) awakening the readers...
awoken 934 (sleepy-testr)
read ok! code = 0
write ok! code = 0
[2] - Done                       ./sleepy-testr
[root@gfy-S3C2440 /tmp]# ./sleepy-testw
File Opened!
process 939 (sleepy-testw) awakening the readers...
awoken 935 (sleepy-testr)
read ok! code = 0
write ok! code = 0
[3] + Done                       ./sleepy-testr
[root@gfy-S3C2440 /tmp]#
上述实验现象用《LDD3》上的话来说就是:读者也许会认为当两个进程在等待时调用sleepy_write(),当一个进程被唤醒时会将flag重置为0,第二个被唤醒的进程会立即进入睡眠状态。在单处理器系统中,这几乎是始终会发生的,正如上述实验结果所示。但是,我们必须理解并不是永远会发生这种情况,wake_up_interruptible调用会唤醒两个休眠的进程,而在重置flag之前,这两个进程完全有可能注意到标志位非零。
二、             阻塞和非阻塞操作
进行数据的读或者写的时候,如果设备或者设备的数据没有准备好的时候,驱动程序就应该是否进行非阻塞 I/O还是阻塞 I/O操作。
全功能的 read 和 write 方法涉及到进程可以决定是进行非阻塞 I/O还是阻塞 I/O操作。明确的非阻塞 I/O 由 filp->f_flags 中的 O_NONBLOCK 标志来指示(定义再 ,被 自动包含)。
不止是read和write方法上有O_NONBLOCK操作,O_NONBLOCK在open方法中也是很有意义的。,后面的章节会涉及此方面内容。
只有read、write和open文件操作受非阻塞标志O_NONBLOCK的影响。
一个阻塞I\O的例子
这个例子来自 scullpipe 驱动; 它是 scull 的一个特殊形式, 实现了一个象管道的设备。
该设备驱动程序使用了一个包含两个等待队列和一个缓冲区的设备结构,缓冲区大小可以用通常的方式配置(编译或加载),如下:
struct scull_pipe
{
        wait_queue_head_t inq, outq; /* read and write queues */
        char *buffer, *end; /* begin of buf, end of buf */
        int buffersize; /* used in pointer arithmetic */
        char *rp, *wp; /* where to read, where to write */
        int nreaders, nwriters; /* number of openings for r/w */
        struct fasync_struct *async_queue; /* asynchronous readers 异步读取者*/
        struct semaphore sem;  /* mutual exclusion semaphore 互斥信号量 */
        struct cdev cdev;  /* Char device structure */
};
Read实现负责阻塞和非阻塞型输入,如下:
static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t
*f_pos)
{
        struct scull_pipe *dev = filp->private_data;
        if (down_interruptible(&dev->sem))
                return -ERESTARTSYS;
        while (dev->rp == dev->wp)
        { /* nothing to read */
                up(&dev->sem); /* release the lock */
                if (filp->f_flags & O_NONBLOCK)
                        return -EAGAIN;
                PDEBUG("\"%s\" reading: going to sleep\n", current->comm);
                if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
                        return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */
                if (down_interruptible(&dev->sem))
                        return -ERESTARTSYS;
        }
        /* ok, data is there, return something */
        if (dev->wp > dev->rp)
                count = min(count, (size_t)(dev->wp - dev->rp));
        else /* the write pointer has wrapped, return data up to dev->end */
                count = min(count, (size_t)(dev->end - dev->rp));
        if (copy_to_user(buf, dev->rp, count))
        {
                up (&dev->sem);
                return -EFAULT;
        }
        dev->rp += count;
        if (dev->rp == dev->end)
                dev->rp = dev->buffer; /* wrapped */
        up (&dev->sem);
        /* finally, awake any writers and return */
        wake_up_interruptible(&dev->outq);
        PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);
        return count;
}
好,现在需要好好理解上述阻塞原理,我还未完全理解,只把程序整理出来了,应用程序就用的前辈的,由于驱动程序我是在原先的scull设备里面把read方法修改成了阻塞方式,所以只生成了一个设备节点,应用程序里面就需要删除一个节点,驱动和应用程序源码如下:
PS:我看书的思路是,先把整体思路看清晰了,然后就开始构建代码,当别人的应用程序能完全在我构建的驱动上运行,再去分析细节地方;

/*
*PS: Device Driver
*File Name :scullpipe.c
*Function :test character device driver, only register one scull
*Author :gufeiyang
*From :<>
*Time :2010-2-14 home yunnan
*/

#include <linux/module.h> /*EXPORT_SYMBOL_GPL ())*/
#include <linux/moduleparam.h> /*module_param(variable, type, perm); */
#include <linux/init.h> /*module_init(init_function); module_exit(cleanup_function); */
#include <linux/kernel.h> /*int printk(const char * fmt, ...);*/
#include <linux/types.h> /* size_t */
#include <linux/kdev_t.h> /*dev_t*/
#include <linux/fs.h> /* everything... */
#include <linux/cdev.h> /*struct cdev *cdev_alloc(void); */
#include <linux/slab.h> /* kmalloc() */
#include <linux/device.h> /*struct class*/
#include <asm/uaccess.h> /* copy_*_user */
#include <linux/wait.h>
#include<linux/ioctl.h>
#include "scullpipe.h"
static int scull_major = SCULL_MAJOR;
static int scull_minor = 0;
static int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */
static int scull_quantum = SCULL_QUANTUM;
static int scull_qset = SCULL_QSET;
static int scull_p_buffer = 4000; /* buffer size */
module_param(scull_nr_devs, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);
struct scull_dev my_dev;
static struct class *scull_class;
 int scull_trim(struct scull_dev *dev)
{
         struct scull_qset *next, *dptr;
         int qset = dev->qset; /* "dev" is not-null */
         int i;
         for (dptr = dev->data; dptr; dptr = next)
         { /* all the list items */
                   if (dptr->data) {
                            for (i = 0; i < qset; i++)
                                     kfree(dptr->data[i]);
                            kfree(dptr->data);
                            dptr->data = NULL;
                   }
                   next = dptr->next;
                   kfree(dptr);
         }
         dev->size = 0;
         dev->quantum = scull_quantum;
         dev->qset = scull_qset;
         dev->data = NULL;
         return 0;
}
static struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
         struct scull_qset *qs = dev->data;
         /* Allocate first qset explicitly if need be */
         if (! qs) {
                   qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
                   if (qs == NULL)
                            return NULL; /* Never mind */
                   memset(qs, 0, sizeof(struct scull_qset));
         }
         /* Then follow the list */
         while (n--) {
                   if (!qs->next) {
                            qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
                            if (qs->next == NULL)
                                     return NULL; /* Never mind */
                            memset(qs->next, 0, sizeof(struct scull_qset));
                   }
                   qs = qs->next;
                   continue;
         }
         return qs;
}
static ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
         struct scull_dev *dev = filp->private_data;
         struct scull_qset *dptr;
         int quantum = dev->quantum, qset = dev->qset;
         int itemsize = quantum * qset;
         int item, s_pos, q_pos, rest;
         ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
         if (down_interruptible(&dev->sem))
                   return -ERESTARTSYS;
         /* find listitem, qset index and offset in the quantum */
         item = (long)*f_pos / itemsize;
         rest = (long)*f_pos % itemsize;
         s_pos = rest / quantum;
         q_pos = rest % quantum;
         /* follow the list up to the right position */
         dptr = scull_follow(dev, item);
         if (dptr == NULL)
                   goto out;
         if (!dptr->data)
         {
                   dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
                   if (!dptr->data)
                            goto out;
                   memset(dptr->data, 0, qset * sizeof(char *));
         }
         if (!dptr->data[s_pos])
         {
                   dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
                   if (!dptr->data[s_pos])

                            goto out;
         }
         /* write only up to the end of this quantum */
         if (count > quantum - q_pos)

                   count = quantum - q_pos;
         if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
         {
                   retval = -EFAULT;
                   goto out;

         }
         *f_pos += count;
         retval = count;

         /* update the size */
         if (dev->size < *f_pos)
                   dev->size = *f_pos;

out:
                   up(&dev->sem);
        return retval;
}
static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
         struct scull_dev *dev = filp->private_data;
         if (down_interruptible(&dev->sem))
                   return -ERESTARTSYS;

         while (dev->rp == dev->wp)
         { /* nothing to read */
                   up(&dev->sem); /* release the lock */
                   if (filp->f_flags & O_NONBLOCK)

                            return -EAGAIN;
                   PDEBUG("\"%s\" reading: going to sleep\n", current->comm);
                   if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
                            return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */
                   PDEBUG("I am Here\n");
                   if (down_interruptible(&dev->sem))
                            return -ERESTARTSYS;
         }
         /* ok, data is there, return something */

         if (dev->wp > dev->rp)
                   count = min(count, (size_t)(dev->wp - dev->rp));
         else /* the write pointer has wrapped, return data up to dev->end */
                   count = min(count, (size_t)(dev->end - dev->rp));
         if (copy_to_user(buf, dev->rp, count))
         {
                   up (&dev->sem);
                   return -EFAULT;
         }
         dev->rp += count;
         if (dev->rp == dev->end)

                   dev->rp = dev->buffer; /* wrapped */
         up (&dev->sem);

         /* finally, awake any writers and return */
         wake_up_interruptible(&dev->outq);
         PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count);
         return count;
}

static int scull_open(struct inode *inode, struct file *filp)
{
         struct scull_dev *dev; /* device information */
         printk(KERN_WARNING"Module Opened![kernel]\n");
         dev = container_of(inode->i_cdev, struct scull_dev, cdev);
         filp->private_data = dev; /* for other methods */
         /* now trim to 0 the length of the device if open was write-only */
         if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
         {
                            scull_trim(dev); /* ignore errors */
         }
         init_waitqueue_head(&dev->inq);
         init_waitqueue_head(&dev->outq);
// printk(KERN_WARNING"open sem = %d\n",(int)&dev->sem);

         if (down_interruptible(&dev->sem))
                   return -ERESTARTSYS;
         /*added for scullpipe*/
         if (!dev->buffer) {
                   /* allocate the buffer */
                   dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL);
                   if (!dev->buffer) {
                            up(&dev->sem);
                            return -ENOMEM;
                   }
         }
         dev->buffersize = scull_p_buffer;
         dev->end = dev->buffer + dev->buffersize;
         dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */

         /* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */
         if (filp->f_mode & FMODE_READ)
                   dev->nreaders++;
         if (filp->f_mode & FMODE_WRITE)
                   dev->nwriters++;
         up(&dev->sem);
         return nonseekable_open(inode, filp); /* success */
}
static int scull_p_fasync(int fd, struct file *filp, int mode)
{
         struct scull_dev *dev = filp->private_data;
         return fasync_helper(fd, filp, mode, &dev->async_queue);
}
static int scull_release(struct inode *inode, struct file *filp)
{
         struct scull_dev *dev = filp->private_data;
         /* remove this filp from the asynchronously notified filp's */
         scull_p_fasync(-1, filp, 0);
         down(&dev->sem);
         if (filp->f_mode & FMODE_READ)
                   dev->nreaders--;
         if (filp->f_mode & FMODE_WRITE)
                   dev->nwriters--;
         if (dev->nreaders + dev->nwriters == 0) {
                   kfree(dev->buffer);
                   dev->buffer = NULL; /* the other fields are not checked on open */
         }
         up(&dev->sem);
         printk(KERN_WARNING"\nModule Released![kernel]\n");
         return 0;
}
static int scull_p_ioctl(struct inode *inode, struct file *filp,
                                       unsigned int cmd, unsigned long arg)
{

         int err = 0;
         /*
         * extract the type and number bitfields, and don't decode
         * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
         */

         if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
         if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
         /*
         * the direction is a bitmask, and VERIFY_WRITE catches R/W
         * transfers. `Type' is user-oriented, while
         * access_ok is kernel-oriented, so the concept of "read" and
         * "write" is reversed
         */

         if (_IOC_DIR(cmd) & _IOC_READ)
                   err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
         else if (_IOC_DIR(cmd) & _IOC_WRITE)
                   err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
         if (err) return -EFAULT;
         switch(cmd) {
        /*
                   * The following two change the buffer size for scullpipe.
                   * The scullpipe device uses this same ioctl method, just to
                   * write less code. Actually, it's the same driver, isn't it?
                   */

                   case SCULL_P_IOCTSIZE:
                            scull_p_buffer = arg;
                            break;
                   case SCULL_P_IOCQSIZE:
                            return scull_p_buffer;
                            default: /* redundant, as cmd was checked against MAXNR */
                                     return -ENOTTY;
         }
         return 0;
}
struct file_operations scull_fops = {
         .owner = THIS_MODULE,
// .llseek = scull_llseek,

         .read = scull_p_read,
                            .write = scull_write,
                            .ioctl = scull_p_ioctl,
         .open = scull_open,
         .release = scull_release
};
/*
 * Set up the char_dev structure for this device.
 */

static int scull_setup_cdev(struct scull_dev *dev)
{
         int err = 1, devno = MKDEV(scull_major, scull_minor );

         /*init data*/
         dev->quantum = scull_quantum;
         dev->qset = scull_qset;
         init_MUTEX(&dev->sem);
         
         /*register*/
         cdev_init(&dev->cdev, &scull_fops);
         dev->cdev.owner = THIS_MODULE;
         dev->cdev.ops = &scull_fops;
         err = cdev_add (&dev->cdev, devno, 1);
         /* Fail gracefully if need be */
         if (err)
         {
                   printk(KERN_NOTICE "Error %d adding scullc\n", err);
                   return err;
         }
         return 0;//ok

}
static int __init scull_init(void)
{
         int result,ret;
         dev_t dev;
         PDEBUG("Debug Opend!\n");
/*
         * Get a range of minor numbers to work with, asking for a dynamic
         * major unless directed otherwise at load time.
 */

         if (scull_major) {
                   dev = MKDEV(scull_major, scull_minor);
                   result = register_chrdev_region(dev, scull_nr_devs, DEV_NAME);
         } else {
                   result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, DEV_NAME);
                   scull_major = MAJOR(dev);
         }
         if (result < 0) {
                   printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
                   return result;
         }else {
                   printk(KERN_WARNING"Scull_major = %d\n", scull_major);
         }
         /*注册设备*/
// ret = register_chrdev(scull_major, DEV_NAME, &scull_fops);//老方法

         ret = scull_setup_cdev(&my_dev);//新方法

         if (ret < 0)
         {
                   printk(KERN_WARNING " can't register major number\n");
                   return ret;
         }
         //printk(KERN_WARNING"init sem = %d\n",(int)&my_dev.sem);

         /*注册一个类,使mdev可以在/dev下创建设备节点*/
         scull_class = class_create(THIS_MODULE, DEV_NAME);
         if(IS_ERR(scull_class))
         {
                   printk(KERN_ALERT"Err:faile in scull_class!\n");
                   return -1;
         }
         /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/
         class_device_create(scull_class, NULL, dev, NULL, DEV_NAME);
         printk(KERN_WARNING"Module Initialed!\nChracter Device Driver Start!\n");
         return 0;
}
static void __exit scull_exit(void)
{
         int devno = MKDEV(scull_major, scull_minor );
         
         unregister_chrdev_region(devno, scull_nr_devs);
         cdev_del(&my_dev.cdev);
         
         class_device_destroy(scull_class, devno);
         class_destroy(scull_class);
         printk(KERN_WARNING"Module exit!\nChracter Device Driver End!\n");
}
module_init(scull_init);
module_exit(scull_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CHARACTER DEVICE DRVER");
MODULE_AUTHOR("gufeiyang@2010-02-14");
/*scullpipe.h*/
/*
*File Name : scull.h
*/

#ifndef _SCULL_H_
#define _SCULL_H_
#define DEV_NAME "scullpipe"
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0 /* dynamic major by default */
#endif
#ifndef SCULL_NR_DEVS
#define SCULL_NR_DEVS 1 /* scull */
#endif
/* Macros to help debugging */
#undef PDEBUG /* undef it, just in case */
#ifdef SCULL_DEBUG
# ifdef __KERNEL__
     /* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull[kernel]: " fmt, ## args)
# else /* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
/*
 * The bare device is a variable-length region of memory.
 * Use a linked list of indirect blocks.
 *
 * "scull_dev->data" points to an array of pointers, each
 * pointer refers to a memory area of SCULL_QUANTUM bytes.
 *
 * The array (quantum-set) is SCULL_QSET long.
 */

#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif
#ifndef SCULL_QSET
#define SCULL_QSET 1000
#endif
/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC 'k'
/* Please use a different 8-bit number in your code */
#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)
/*
 * S means "Set" through a ptr,
 * T means "Tell" directly with the argument value
 * G means "Get": reply by setting through a pointer
 * Q means "Query": response is on the return value
 * X means "eXchange": switch G and S atomically
 * H means "sHift": switch T and Q atomically
 */

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)
#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)
#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)
#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)
#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)
#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)
#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)
#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)
#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)
#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)
#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)
#define SCULL_IOC_MAXNR 14
/*
 * Ioctl definitions
 */

/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC 'k'
/* Please use a different 8-bit number in your code */
#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 0)
#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 1)
/* ... more to come */
/*
 * Representation of scull quantum sets.
 */

struct scull_qset {
         void **data;
         struct scull_qset *next;
};
struct scull_dev {
         struct scull_qset *data; /* Pointer to first quantum set */
         int quantum; /* the current quantum size */
         int qset; /* the current array size */
         unsigned long size; /* amount of data stored here */
         unsigned int access_key; /* used by sculluid and scullpriv */
         struct semaphore sem; /* mutual exclusion semaphore */
         
         /*added for scullpipe*/
         wait_queue_head_t inq, outq; /* read and write queues */
         char *buffer, *end; /* begin of buf, end of buf */
         int buffersize; /* used in pointer arithmetic */
         char *rp, *wp; /* where to read, where to write */
         int nreaders, nwriters; /* number of openings for r/w */
         struct fasync_struct *async_queue; /* asynchronous readers */
         /*add end*/
         struct cdev cdev; /* Char device structure */
};
#endif
/*application*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <stropts.h>
#include <linux/poll.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "scullpipe.h"
int main()
{
         char buffer1[20]={0};
         int pipetest0, pipetest1;
         int code=21, i=0;
         struct pollfd poll_list[2];
         int retval;
         if ((pipetest0 = open("/dev/scullpipe",O_RDONLY)) < 0) {
                   printf("open scullpipe0 error! \n");
                   exit(1);
         }
         printf("open scullpipe0 ! \n");
         if ( ioctl(pipetest0 , SCULL_P_IOCTSIZE , code ) < 0) {
                   printf("pipetest0 ioctl SCULL_P_IOCTSIZE error! \n");
         exit(1);
         }
         printf(" SCULL_P_IOCTSIZE : scull_p_buffer0=%d !\n" ,ioctl( pipetest0 , SCULL_P_IOCQSIZE , NULL ) );
         close(pipetest0);
         printf("close pipetest0 ! \n");
         if ((pipetest0 = open("/dev/scullpipe",O_RDONLY)) < 0) {
                   printf("reopen scullpipe0 error! \n");
                   exit(1);
         }
         printf("reopen scullpipe0 ! \n");
         poll_list[0].fd = pipetest0;
         poll_list[0].events = POLLIN|POLLRDNORM;
         while(1)
         {
                   retval = poll(poll_list,(unsigned long)2,-1);
                   /* retval 总是大于0或为-1,因为我们在阻塞中工作 */
  
                   if(retval < 0)
                   {
                            fprintf(stderr,"poll错误: %s\n",strerror(errno));
                            return -1;
                   }
    
                   if(poll_list[0].revents&(POLLIN|POLLRDNORM))
                   {
                            if ((code=read(pipetest0 , buffer1 , 20)) != 20)
                                     printf("read from pipetest0 error! code=%d\n",code);
                            else
                                     printf("read from pipetest0 ok! code=%d \n",code);
                                     
                            for(i=0;i<20;i+=5)
                                     printf("[%d]=%c [%d]=%c [%d]=%c [%d]=%c [%d]=%c\n",i,buffer1[i],i+1,buffer1[i+1],i+2,buffer1[i+2],i+3,buffer1[i+3],i+4,buffer1[i+4]);
                            
                   }
         }
         close(pipetest0 );
         printf("close pipetest0 ! \n");
         
         printf("\n");
         exit(0);
}

上述自己修改的源码有个问题,写的时候未能和阻塞联系上,需要好好理解整个过程。
阅读(1629) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~