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

         参考《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);
    }
}







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值