您的位置:首页 > 运维架构 > Linux

scull0字符设备驱动示例(参考《Linux设备驱动程序》)

2012-02-19 22:52 369 查看
参考《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");

/*Makefile*/
ifneq ($(KERNELRELEASE),)
obj-m := scull.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif

/*加载脚本*/
#!/bin/sh
module="scull"
device="scull"
mode="664"

if grep '^staff:' /etc/group > /dev/null; then    #并非所有的发行版本都有staff组,有些有wheel组
group="staff"
else
group="wheel"
fi

/sbin/insmod -f ./$module.ko $* || exit 1    #调用insmod,并将所有传入该脚本的参数传给模块,同时使用路径名来指定模块的位置,因为新的modutils默认不会在当前的目录中查找模块

major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"`

rm -f /dev/${device}[0-3]    #删除原有节点

mknod /dev/${device}0 c $major 0    #创建新的节点
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
#ln -sf ${device}0 /dev/${device}

chgrp $group /dev/${device}[0-3]    #给定适当的组属性及许可,并修改属组
chmod $mode  /dev/${device}[0-3]

/*卸载脚本*/
#!/bin/sh
module="scull"
device="scull"

/sbin/rmmod $module $* || exit 1

rm -f /dev/${device} /dev/${device}[0-3]

/*测试程序*/
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char **argv)
{
int fd = -1;
int count = 0;
char buf[30];
char *data = "it just for test scull0 ";

fd = open("/dev/scull0",O_RDWR);
if(fd < 0)
{
printf("error %d:can't open device scull0\n",fd);
return -1;
}
else
printf("fd:%d\n",fd);

while(1)
{
memset(buf,'\0',sizeof(buf));
count = write(fd,data,strlen(data));
if(count < 0)
{
printf("error%d,can't write data to device scull0\n",count);
continue;
}

count = read(fd,buf,strlen(data));
if(count < 0)
{
printf("can't read data from device scull0,count:%d\n",count);
}
else
{
printf("num:%d,read data:%s \n",count,buf);
}

sleep(1);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: