Linux驱动开发之经典字符驱动实例分析

1、驱动源代码

#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/sched.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>


//#include "scull.h" /* local definitions */
static int scull_p_major=250;
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 semaphore sem;              /* mutual exclusion semaphore */
        struct cdev cdev;                  /* Char device structure */
};


/* parameters */


int scull_p_buffer =  0x4000; /* buffer size */
dev_t scull_p_devno; /* Our first device number */




module_param(scull_p_buffer, int, 0);


static struct scull_pipe*  scull_p_devices;
static int spacefree(struct scull_pipe *dev);
/*
 * Open and close
 */




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);//获得scull_pipe设备结构体的地址
filp->private_data = dev;  //把设备结构体地址保存到file中


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






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




/*
 * Data management: read and write
 */


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; //返回读的数据
}


/* Wait for space for writing; caller must hold device semaphore.  On
 * error the semaphore will be released before returning. */
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);//把当前进程放在等待队列中,把当前的进程状态标识位修改为 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() */


/* and signal asynchronous readers, explained late in chapter 5 */
return count;
}






/* FIXME this should use seq_file */




/*
 * The file operations for the pipe device
 * (some are overlayed with bare scull)
 */
struct file_operations scull_pipe_fops = {
.owner = THIS_MODULE,
.read = scull_p_read,
.write = scull_p_write,
.open = scull_p_open,
.release = scull_p_release,
};




/*
 * Set up a cdev entry.
 */
static void scull_p_setup_cdev(struct scull_pipe *dev, int minor)
{
int err, devno = MKDEV(scull_p_major, minor);
    
cdev_init(&dev->cdev, &scull_pipe_fops);  //初始化cdev
dev->cdev.owner = THIS_MODULE;
err = cdev_add (&dev->cdev, devno, 1);  //注册cdev到内核
/* Fail gracefully if need be */
if (err)
printk("Error adding scullpipe");
}


 


/*
 * Initialize the pipe devs; return how many we did.
 */
int scull_p_init(void)
{
int result;


dev_t firstdev = MKDEV(scull_p_major, 0);




result = register_chrdev_region(firstdev, 1, "scullp"); //静态注册设备号
if (result < 0) {
printk(KERN_NOTICE "Unable to get scullp region, error %d\n", result);
return 0;
}


scull_p_devices = kmalloc(sizeof(struct scull_pipe), GFP_KERNEL); //分配设备结构体
if (scull_p_devices == NULL) {
unregister_chrdev_region(firstdev, 1);
return 0;
}
memset(scull_p_devices, 0, sizeof(struct scull_pipe));//清空设备结构体


init_waitqueue_head(&scull_p_devices->inq);  //初始化等待队列
init_waitqueue_head(&scull_p_devices->outq);
init_MUTEX(&scull_p_devices->sem);   //初始化信号量
scull_p_setup_cdev(scull_p_devices , 0);

printk("pipe device installed, with major %d\n", scull_p_major);
return 0;
}


/*
 * This is called by cleanup_module or on failure.
 * It is required to never fail, even if nothing was initialized first
 */
void scull_p_cleanup(void)
{
int i;




if (!scull_p_devices)
return; /* nothing else to release */




cdev_del(&scull_p_devices->cdev);
kfree(scull_p_devices->buffer);

kfree(scull_p_devices);
unregister_chrdev_region(MKDEV(scull_p_major,0), 1);
scull_p_devices = NULL; /* pedantic */
printk("pipe device was removed!\n");
}
module_init(scull_p_init);

module_exit(scull_p_cleanup);

2、Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:                               
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:                                             
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
else
    obj-m := memdev.o


endif

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80890572