您的位置:首页 > 其它

高级字符驱动程序操作之poll(实践篇)

2010-12-28 14:32 477 查看
1. scull.h 驱动程序由scullpipe改过来的

#ifndef _SCULL_H_
#define _SCULL_H_

#ifndef SCULL_P_NR_DEVS
#define SCULL_P_NR_DEVS 2
#endif

#ifndef SCULL_P_BUFFER
#define SCULL_P_BUFFER 4000
#endif

extern int scull_p_buffer;

int     scull_p_init(void);
void    scull_p_cleanup(void);

#endif /* _SCULL_H_ */


2. poll.c

#include <linux/module.h>
#include <linux/moduleparam.h>

#include <linux/kernel.h>	/* printk(), min() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/proc_fs.h>
#include <linux/errno.h>	/* error codes */
#include <linux/types.h>	/* size_t */
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

#include "scull.h"		/* local definitions */

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 */
};

/* parameters */
static int scull_p_nr_devs = SCULL_P_NR_DEVS;	/* number of pipe devices */
int scull_p_buffer =  SCULL_P_BUFFER;	/* buffer size */
static int poll_major = 0;

static struct scull_pipe *scull_p_devices;

static int spacefree(struct scull_pipe *dev);

static int scull_p_open(struct inode *inode, struct file *filp)
{
struct scull_pipe *dev;

dev = container_of(inode->i_cdev, struct scull_pipe, cdev);
filp->private_data = dev;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
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);
}

static int scull_p_release(struct inode *inode, struct file *filp)
{
struct scull_pipe *dev = filp->private_data;

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);
return 0;
}

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;

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);

return count;
}

static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
{
while (spacefree(dev) == 0) { /* full */
DEFINE_WAIT(wait);

up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;

prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
if (spacefree(dev) == 0)
schedule();
finish_wait(&dev->outq, &wait);
if (signal_pending(current))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
return 0;
}

/* How much space is free? */
static int spacefree(struct scull_pipe *dev)
{
if (dev->rp == dev->wp)
return dev->buffersize - 1;
return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1;
}

static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_pipe *dev = filp->private_data;
int result;

if (down_interruptible(&dev->sem))
return -ERESTARTSYS;

/* Make sure there's space to write */
result = scull_getwritespace(dev, filp);
if (result)
return result; /* scull_getwritespace called up(&dev->sem) */

/* ok, space is there, accept something */
count = min(count, (size_t)spacefree(dev));
if (dev->wp >= dev->rp)
count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */
else /* the write pointer has wrapped, fill up to rp-1 */
count = min(count, (size_t)(dev->rp - dev->wp - 1));

if (copy_from_user(dev->wp, buf, count)) {
up (&dev->sem);
return -EFAULT;
}
dev->wp += count;
if (dev->wp == dev->end)
dev->wp = dev->buffer; /* wrapped */
up(&dev->sem);

/* finally, awake any reader */
wake_up_interruptible(&dev->inq);  /* blocked in read() and select() */

return count;
}

static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
struct scull_pipe *dev = filp->private_data;
unsigned int mask = 0;

down(&dev->sem);
poll_wait(filp, &dev->inq,  wait);
poll_wait(filp, &dev->outq, wait);
if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM;	/* readable */
if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM;	/* writable */
up(&dev->sem);
return mask;
}

struct file_operations scull_pipe_fops = {
.owner =	THIS_MODULE,
.llseek =	no_llseek,
.read =		scull_p_read,
.write =	scull_p_write,
.poll =		scull_p_poll,
.open =		scull_p_open,
.release =	scull_p_release,
};

int scull_p_init(void)
{
int result;
int i;
dev_t dev = 0;
struct scull_pipe *device;

if (poll_major) {
dev = MKDEV(poll_major, 0);
result = register_chrdev_region(dev, 1, "poll");
} else {
result = alloc_chrdev_region(&dev, 0, 1, "poll");
poll_major = MAJOR(dev);
}
if (result < 0) {
return result;
}

scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe), GFP_KERNEL);
if (scull_p_devices == NULL) {
result = -ENOMEM;
goto fail;
}
memset(scull_p_devices, 0, scull_p_nr_devs * sizeof(struct scull_pipe));
for (i = 0; i < scull_p_nr_devs; i++) {
init_waitqueue_head(&(scull_p_devices[i].inq));
init_waitqueue_head(&(scull_p_devices[i].outq));
init_MUTEX(&scull_p_devices[i].sem);

device = scull_p_devices + i;
cdev_init(&device->cdev, &scull_pipe_fops);
device->cdev.owner = THIS_MODULE;
cdev_add(&device->cdev, dev + i, 1);
}

return scull_p_nr_devs;

fail:
scull_p_cleanup();
return result;
}

void scull_p_cleanup(void)
{
int i;
dev_t devno;
devno = MKDEV(poll_major, 0);

if (!scull_p_devices)
return;

for (i = 0; i < scull_p_nr_devs; i++) {
cdev_del(&scull_p_devices[i].cdev);
kfree(scull_p_devices[i].buffer);
}
kfree(scull_p_devices);
unregister_chrdev_region(devno, scull_p_nr_devs);
scull_p_devices = NULL; /* pedantic */
}

MODULE_AUTHOR("victorsummer");
MODULE_LICENSE("Dual BSD/GLP");

module_init(scull_p_init);
module_exit(scull_p_cleanup);


3. Makefile

KERNELDIR = /usr/src/linux-headers-2.6.31-14-generic
PWD := $(shell pwd)

obj-m := poll.o

modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules


4. poll_testw.c 带参数,0表示打开第一设备,1表示打开第二个设备

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
char *write_buffer = "Hello, Character driver!";
int poll_fd;
int code;

if(argc == 2 && !strcmp(argv[1],"1"))

poll_fd = open("/dev/poll_pipe1", O_WRONLY);
else if(argc == 2 && !strcmp(argv[1],"0"))
poll_fd = open("/dev/poll_pipe0", O_WRONLY);
else
{
printf("You have to input option 0, 1 or all! /n");
return;
}

while(*write_buffer != '/0')
{
code = write(poll_fd , write_buffer , 24);
printf("Write %d bytes to polltest/n", code);
write_buffer += code;
}

close(poll_fd);

exit(0);
}


5. poll_testr.c

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/poll.h>

int main()
{
int polltest0, polltest1;

int retval;
struct pollfd poll_list[2];

polltest0 = open("/dev/poll_pipe0", O_RDONLY);
polltest1 = open("/dev/poll_pipe1", O_RDONLY);

poll_list[0].fd = polltest0;
poll_list[1].fd = polltest1;
poll_list[0].events = POLLIN | POLLRDNORM;
poll_list[1].events = POLLIN | POLLRDNORM;

while(1)
{
retval = poll(poll_list, 2, -1);

if(retval < 0)
{
printf("Error in poll! /n");
return -1;
}

if(poll_list[0].revents&(POLLIN | POLLRDNORM))
{
char *read_buffer = malloc(sizeof(char)*30);
if (read(polltest0, read_buffer, 24) != -1)
{
printf("read %s from polltest0 /n", read_buffer);
}
free(read_buffer);
}

if(poll_list[1].revents&(POLLIN | POLLRDNORM))
{
char *read_buffer = malloc(sizeof(char)*30);
if (read(polltest1, read_buffer, 24) != -1)
{
printf("read %s from polltest1 /n", read_buffer);
}
free(read_buffer);
}
}

close(polltest0 );
close(polltest1 );

exit(0);
}


6. Makefile

all : poll_testw.o poll_testr.o
gcc -o poll_testw.o poll_testw.c
gcc -o poll_testr.o poll_testr.c


7. 开始测试

装载驱动程序

sudo insmod ./poll.ko

查看主设备号,这里假设为251

cat /proc/devices

建立设备节点

sudo mknod /dev/poll_pipe0 c 251 0

sudo mknod /dev/poll_pipe1 c 251 1

更改权限

sudo chgrp staff /dev/poll_pipe[0-1]

sudo chmod 664 /dev/poll_pipe[0-1]

在第一个终端打开读程序,程序阻塞在poll函数

sudo ./poll_testr.o

在第二个终端打开带参数为0的写程序

sudo ./poll_testw.o 0

结果:

第二个终端打印:Write 24 bytes to polltest

第一个终端打印:read Hello, Character driver! from polltest0

接着在第二个终端打开参数为1的写程序

sudo ./poll_testw.o 1

结果:

第二个终端打印:Write 24 bytes to polltest

第一个终端打印:read Hello, Character driver! from polltest1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: