LDD3《Linux設備驅動》中的最簡單的字符設備驅動實現與測試

源代碼如下:
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>

#include <linux/cdev.h>
#include <asm/uaccess.h>
//#include "scull.h"

#define SCULL_MAJOR 0   /* dynamic major by default */
#define SCULL_NR_DEVS 4    /* scull0 through scull3 */
#define SCULL_P_NR_DEVS 4  /* scullpipe0 through scullpipe3 */
#define SCULL_QUANTUM 4000
#define SCULL_QSET    1000

struct scull_qset {
	void **data;
	struct scull_qset *next;
};

struct scull_dev {
	struct scull_qset *data;  /* Pointer to first quantum set */
	int quantum;              /* the current quantum size */
	int qset;                 /* the current array size */
	unsigned long size;       /* amount of data stored here */
	unsigned int access_key;  /* used by sculluid and scullpriv */
	struct semaphore sem;     /* mutual exclusion semaphore     */
	struct cdev cdev;	  /* Char device structure		*/
};

int	scull_major = SCULL_MAJOR;
int	scull_minor = 0;
int	scull_nr_devs = SCULL_NR_DEVS;
int	scull_quantum = SCULL_QUANTUM;
int	scull_qset = SCULL_QSET;

module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_nr_devs,int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset,int,S_IRUGO);

MODULE_AUTHOR("BG2BKK");
MODULE_LICENSE("Dual BSD/GPL");


struct scull_dev *scull_devices;

int	scull_trim(struct scull_dev *dev)
{
	struct	scull_qset *next,*dptr;
	int	qset = dev->qset;
	int 	i;
	for(dptr = dev->data; dptr;dptr = next)
	{
		if(dptr->data){
			for (i=0;i<qset;i++)
				kfree(dptr->data[i]);
			kfree(dptr->data);
			dptr->data = NULL;
		}
		next = dptr->next;
		kfree(dptr);
	}
	dev->size	= 0;
	dev->quantum	= scull_quantum;
	dev->qset	= scull_qset;
	dev->data	= NULL;
	return 0;
}

int	scull_open(struct inode *inode, struct file *filp)
{
	struct scull_dev *dev;
	dev = container_of(inode->i_cdev,struct scull_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)
{
	printk(KERN_ALERT "scullrelease\n");
	return 0;
}

struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
	struct scull_qset  *qs = dev->data;

	if(!qs)
	{
		qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
		if(qs == NULL)
			return NULL;
		memset(qs,0,sizeof(struct scull_qset));
	}
	
	while(n--)
	{
		if( !qs->next ){
			qs->next = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
			if(qs->next == NULL)
				return NULL;
			memset(qs->next, 0 ,sizeof(struct scull_qset));
		}
		qs = qs->next;
		continue;
	}
	return qs;
}

ssize_t	scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	struct	scull_dev *dev = filp->private_data;
	struct	scull_qset *dptr;
	int	quantum = dev->quantum, qset = dev->qset;
	int	itemsize = quantum*qset;
	int	item,s_pos,q_pos,rest;
	ssize_t	retval = 0;

	if(down_interruptible(&dev->sem))
		return -ERESTARTSYS;
//	printk("f_pos= %d\n",*f_pos);
//	printk("count= %d\n",count);
	if( *f_pos >= dev->size)
		goto out;
	if( *f_pos + count > dev->size)
		count = dev->size - *f_pos;
	item	= (long)*f_pos / itemsize;
	rest	= (long)*f_pos % itemsize;
	s_pos	= rest / quantum;
	q_pos	= rest % quantum;

	dptr	= scull_follow(dev,item);

	if(dptr == NULL || !dptr->data || !dptr->data[s_pos])
		goto out;
	if(count > quantum - q_pos)
		count = quantum - q_pos;
	if(copy_to_user(buf, dptr->data[s_pos] + q_pos, count)){
		retval = -EFAULT;
		goto out;
	}
//	printk("read scull: %d\n",count);
	*f_pos += count;
	retval = count;

out:
	up(&dev->sem);
	return retval;
}

ssize_t	scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	struct	scull_dev *dev = filp->private_data;
	struct	scull_qset *dptr;
	int	quantum = dev->quantum,qset = dev->qset;
	int	itemsize = quantum * qset;
	int	item,s_pos,q_pos,rest;
	ssize_t	retval = -ENOMEM;

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

	item	= (long)*f_pos / itemsize;
	rest	= (long)*f_pos % itemsize;
	s_pos	= rest / quantum;
	q_pos	= rest % quantum;

	dptr	= scull_follow(dev, item);
	if(dptr == NULL)
		goto out;
	if(!dptr->data){
		dptr->data = kmalloc(qset * sizeof(char *),GFP_KERNEL);
		if(!dptr->data)
			goto out;
		memset(dptr->data, 0, qset*sizeof(char *));
	}
	if(!dptr->data[s_pos])
	{
		dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
		if(!dptr->data[s_pos])
			goto out;
	}
	if(count > quantum - q_pos)
		count = quantum - q_pos;
	if(copy_from_user(dptr->data[s_pos] + q_pos, buf,count)){
		retval = -EFAULT;
		goto out;
	}
	*f_pos += count;
	retval = count;

	if(dev->size < *f_pos)
		dev->size = *f_pos;
out:
	up(&dev->sem);
	return retval;
}

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)
{
	struct scull_dev *dev = filp->private_data;
	loff_t newpos;

	switch(whence)
	{
		case 0:
			newpos = off;
			break;
		case 1:
			newpos = filp->f_pos + off;
			break;
		case 2:
			newpos = dev->size + off;
			break;
		default:
			return -EINVAL;
	}
	if(newpos < 0) return -EINVAL;
	filp->f_pos = newpos;
	return newpos;

}

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

void 	scull_cleanup_module(void)
{
	int i;
	dev_t devno = MKDEV(scull_major,scull_minor);

	if(scull_devices){
		for(i=0; i<scull_nr_devs;i++)
		{
			scull_trim(scull_devices + i);
			cdev_del(&scull_devices[i].cdev);
		}
		kfree(scull_devices);
	}
#ifdef	SCULL_DEBUG
//	scull_remove_proc();
#endif
	unregister_chrdev_region(devno,scull_nr_devs);

//	scull_p_cleanup();
//	scull_access_cleanup();
}

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
	int err,devno=MKDEV(scull_major,scull_minor+index);
	cdev_init(&dev->cdev, &scull_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops	= &scull_fops;
	err = cdev_add(&dev->cdev, devno,1);
	if(err)
		printk(KERN_NOTICE "Error %d adding scull%d",err,index);
}

int	scull_init_module(void)
{
	int	result,i;
	dev_t 	dev=0;

	if(scull_major)
	{
		dev = MKDEV(scull_major,scull_minor);
		result = register_chrdev_region(dev,scull_nr_devs,"scull");
	}
	else{
		result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");	
		scull_major = MAJOR(dev);
	}
	if(result < 0){
		printk(KERN_WARNING "scull: can't get major %d\n",scull_major);
		return result;
	}
	printk(KERN_ALERT "hello scull	major %d\n minor %d\n",scull_major,scull_minor);
	

	scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev),GFP_KERNEL);
	if(!scull_devices){
		result = -ENOMEM;
		goto fail;
	}
	memset(scull_devices, 0,scull_nr_devs*sizeof(struct scull_dev));
	for(i=0; i< scull_nr_devs; i++){
		scull_devices[i].quantum = scull_quantum;
		scull_devices[i].qset = scull_qset;
		init_MUTEX(&scull_devices[i].sem);
		scull_setup_cdev(&scull_devices[i],i);
	}

	dev=MKDEV(scull_major,scull_minor+scull_nr_devs);
//	dev += scull_p_init(dev);
//	dev += scull_access_init(dev);
#ifdef	SCULL_DEBUG
	scull_create_proc();
#endif
	return 0;

fail:
	scull_cleanup_module();
	return result;
	
}

module_init(scull_init_module);
module_exit(scull_cleanup_module)

測試代碼如下

/*************************************************************************
 *fileName:    test.c
 *description: test the myscull.c
 *author:      Hzc
 *create time: 2007-04-20
 *modify info: -
*************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>

/* device path */
char path[] = "/dev/sculldev";
int buf[40];
int rbuf[40];
int i;
int t=1;
int main()
{
int f = open(path, O_WRONLY);
if (f == -1)
{
printf("device open error!\n");
return 1;
}
printf("Input a string to write device \n");
//scanf("%s", buf);

for(i=0;i<40;i++)
{
	buf[i]=i;
	write(f, buf+i,1 );	 /* device wirte */
}

close(f);

f= open(path,O_RDONLY);
printf("Read the string from device...\n");
if (f == -1)
{
printf("device open error!\n");
return 1;
}
for(i=0;i<40;i++)
{
	read(f, rbuf+i, 1);	 /* device wirte */
}
for(i=0;i<40;i++)
	{
		printf("%d\t",rbuf[i]);
		if((i+1)%10 == 0) printf("\n");
	}	


close(f);
}


驅動加載腳本如下:

#!/bin/sh

insmod scull.ko
mknod /dev/sculldev c 252 0
驅動卸載腳本如下:
#!/bin/sh

rmmod scull.ko
rm /dev/sculldev



發佈了51 篇原創文章 · 獲贊 23 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章