参考《Linux设备驱动程序》及其代码,写了一个简单的字符设备驱动,相比作者提供的要简单很多,我在ubuntu10.04的系统上已测试,能运行起来。因水平有限,只实现了open,release,write及read四个基本的接口,且使用测试程序运行时存在明显的bug,望志同道合的朋友一起完善。
头文件部分的定义:
- #ifndef __SCULL_H__
-
#define __SCULL_H__
-
-
#ifndef SCULL_MAJOR
-
#define SCULL_MAJOR 0
-
#endif
-
-
#ifndef SCULL_MINOR
-
#define SCULL_MINOR 0
-
#endif
-
-
typedef struct _scull_dev_ {
-
void **data;
-
int quantum;
-
int qset;
-
unsigned long size;
-
struct semaphore sem;
-
struct cdev cdev;
-
-
} SCULL_DEV,pSCULL_DEV;
-
-
#endif
驱动程序:
- /*
-
* name:scull.c
-
* function:scull character device driver
-
* time:2012-2-19
-
*
-
* author:txgcwm
-
* mail:txgcwm@163.com
-
* reference:Linux设备驱动程序(第三版)
-
*/
-
-
#ifndef __KERNEL__
-
# define __KERNEL__
-
#endif
-
#ifndef MODULE
-
# define MODULE
-
#endif
-
-
#include <linux/init.h>
-
#include <linux/module.h>
-
#include <linux/cdev.h>
-
#include <linux/slab.h>
-
#include <linux/kernel.h> /* printk() */
-
#include <linux/fs.h> /* everything... */
-
#include <linux/errno.h> /* error codes */
-
#include <linux/types.h> /* size_t */
-
#include <linux/proc_fs.h>
-
#include <linux/fcntl.h> /* O_ACCMODE */
-
#include <asm/system.h> /* cli(), *_flags */
-
#include <asm/uaccess.h>
-
-
#include "scull.h" /* local definitions */
-
-
-
int scull_major = SCULL_MAJOR;
-
int scull_minor = SCULL_MINOR;
-
int scull_mn_dev = 1;
-
int scull_quantum = 4000; /*每个指针所指向存储单元的大小*/
-
int scull_qset = 1000; /*指针数组的大小*/
-
static pSCULL_DEV *pscull_dev = NULL;
-
-
-
int scull_trim(SCULL_DEV *dev)
-
{
-
int i = 0;
-
int qset = dev->qset;
-
-
if (dev->data)
-
{
-
for(i = 0; i < qset; i++)
-
{
-
if (dev->data[i])
-
kfree(dev->data[i]);
-
}
-
-
kfree(dev->data);
-
dev->data=NULL;
-
}
-
-
dev->size = 0;
-
dev->quantum = scull_quantum;
-
dev->qset = scull_qset;
-
-
return 0;
-
}
-
-
int scull_open(struct inode *inode, struct file *filp)
-
{
-
SCULL_DEV *dev = NULL;
-
-
dev = container_of(inode->i_cdev,pSCULL_DEV,cdev);
-
filp->private_data = dev;
-
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
-
{
-
scull_trim(dev);
-
}
-
-
return 0;
-
}
-
-
int scull_release(struct inode *inode, struct file *filp)
-
{
-
return 0;
-
}
-
-
ssize_t scull_read(struct file *filp, char *buf, size_t count,loff_t *f_pos)
-
{
-
SCULL_DEV *dev = filp->private_data;
-
int quantum = dev->quantum;
-
int qset = dev->qset;
-
int s_pos, q_pos;
-
ssize_t ret = 0;
-
-
if(down_interruptible(&dev->sem))
-
return -ERESTARTSYS;
-
if(*f_pos >= dev->size)
-
goto out;
-
if(*f_pos + count > dev->size)
-
count = dev->size - *f_pos;
-
-
s_pos = (long)*f_pos / quantum;
-
q_pos = (long)*f_pos % quantum;
-
-
if(s_pos > qset)
-
goto out;
-
if(!dev->data)
-
goto out;
-
if(!dev->data[s_pos])
-
goto out;
-
-
if(count > quantum - q_pos)
-
count = quantum - q_pos;
-
-
if(copy_to_user(buf, dev->data[s_pos]+q_pos, count)) /*将内核空间的数据拷贝到用户空间*/
-
{
-
ret = -EFAULT;
-
goto out;
-
}
-
*f_pos += count;
-
ret = count;
-
-
out:
-
up(&dev->sem);
-
return ret;
-
}
-
-
ssize_t scull_write(struct file *filp, const char *buf, size_t count,loff_t *f_pos)
-
{
-
SCULL_DEV *dev = filp->private_data;
-
int quantum = dev->quantum;
-
int qset = dev->qset;
-
int s_pos,q_pos;
-
ssize_t ret = -ENOMEM;
-
-
if(down_interruptible(&dev->sem))
-
return -ERESTARTSYS;
-
-
s_pos = (long)*f_pos / quantum;
-
q_pos = (long)*f_pos % quantum;
-
-
if(!dev->data)
-
{
-
dev->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
-
if (!dev->data)
-
goto out;
-
memset(dev->data, 0, qset * sizeof(char *));
-
}
-
-
if(!dev->data[s_pos])
-
{
-
dev->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
-
if (!dev->data[s_pos])
-
goto out;
-
}
-
-
if(count > quantum - q_pos)
-
count = quantum - q_pos;
-
-
if(copy_from_user(dev->data[s_pos]+q_pos, buf, count)) /*将用户空间的数据拷贝到内核空间*/
-
{
-
ret = -EFAULT;
-
goto out;
-
}
-
*f_pos += count;
-
ret = count;
-
-
if(dev->size < *f_pos)
-
dev->size = *f_pos;
-
-
out:
-
up(&dev->sem);
-
return ret;
-
}
-
-
int scull_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
-
{
-
return 0;
-
}
-
-
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
-
{
-
return 0;
-
}
-
-
struct file_operations scull_fops = {
-
.owner = THIS_MODULE,
-
.llseek = scull_llseek,
-
.read = scull_read,
-
.write = scull_write,
-
.ioctl = scull_ioctl,
-
.open = scull_open,
-
.release = scull_release,
-
};
-
-
static void scull_cleanup_module(void)
-
{
-
dev_t dev;
-
-
dev = MKDEV(scull_major,scull_minor);
-
cdev_del(&pscull_dev->cdev);
-
unregister_chrdev_region(dev,scull_mn_dev);
-
scull_trim(pscull_dev);
-
kfree(pscull_dev);
-
pscull_dev = NULL;
-
printk(KERN_EMERG"Leave World!\n");
-
-
return;
-
}
-
-
static int scull_init_module(void)
-
{
-
dev_t dev;
-
int result = -1;
-
-
pscull_dev = kmalloc(sizeof(SCULL_DEV),GFP_KERNEL);
-
if(pscull_dev == NULL)
-
{
-
printk(KERN_WARNING"scull: can't kmalloc memory\n");
-
result = -ENOMEM;
-
return result;
-
}
-
memset(pscull_dev,0,sizeof(SCULL_DEV));
-
pscull_dev->data = NULL;
-
pscull_dev->quantum = scull_quantum;
-
pscull_dev->qset = scull_qset;
-
sema_init(&pscull_dev->sem, 1);
-
-
if(scull_major)
-
{
-
dev = MKDEV(scull_major,scull_minor);
-
result = register_chrdev_region(dev,scull_mn_dev,"scull");
-
}
-
else
-
{
-
result = alloc_chrdev_region(&dev,scull_minor,scull_mn_dev,"scull");
-
scull_major = MAJOR(dev);
-
}
-
-
if(result < 0)
-
{
-
printk(KERN_WARNING"scull: can't get major %d\n",scull_major);
-
return result;
-
}
-
-
cdev_init(&pscull_dev->cdev,&scull_fops);
-
pscull_dev->cdev.owner = THIS_MODULE;
-
pscull_dev->cdev.ops = &scull_fops;
-
result = cdev_add(&pscull_dev->cdev,dev,1);
-
if(result)
-
{
-
printk(KERN_NOTICE"Error %d add scull device\n",result);
-
goto fail;
-
}
-
printk(KERN_EMERG"Hello World!\n");
-
return 0;
-
-
fail:
-
scull_cleanup_module();
-
-
return result;
-
}
-
-
-
-
module_init(scull_init_module);
-
module_exit(scull_cleanup_module);
-
-
MODULE_AUTHOR("txgcwm");
-
MODULE_VERSION("scull_v1.0");
-
MODULE_LICENSE("GPL");
附件包含了驱动程序,测试程序,启动及卸载脚本。
参考书籍:
《Linux设备驱动程序第三版》
scull.rar
阅读(1061) | 评论(0) | 转发(0) |