1.scull character driver program/*
* Filename:scull.c
* Function:the character test driver
* Author:dengwei
* Time:2010-4-20
*/
#include
#include
#include
#include /*size_t, dev_t*/
#include /* register_chrdev_region*/
#include
#include
#include /* O_ACCMODE */
#include /*struct cdev*/
#include /*kfree*/
#include /*contander_of*/
#include /* error codes */
#include /* cli(), *_flags */
#include /* copy_*_user */
#include"scull.h"
static dev_t dev;
static int scull_major = SCULL_MAJOR;
static int scull_minor = SCULL_MINOR;
static int scull_nr_devs = SCULL_NR_DEVS;
static int scull_quantum = SCULL_QUANTUM;
static int scull_qset = SCULL_QSET;
//static int result = -1;
static char __initdata info[] = "This is the character test driver!\n";
static struct class *scull_class;
static struct scull_dev *scull_devices; /*allocated in scull_init_module*/
/*
* Empty out the scull device; must be called with the device
* semaphore held.scull_dev
*/
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;
int qset = dev->qset; /* "dev 非空" */
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;
}
/*
* open and close
*/
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /*device infomation*/
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /*for other methods*/
/* now trim to 0 the length of the device if open was write_only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY){
scull_trim(dev);
}
return 0;
}
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*
*Follow the list
*/
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
struct scull_qset *qs = dev->data;
/* Allocate first qset explicitly if need be */
if (! qs) {
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL; /* Never mind */
memset(qs, 0, sizeof(struct scull_qset));
}
/* Then follow the list */
while (n--) {
if (!qs->next) {
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
/*
* read and write
*/
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;
if (*f_pos >= dev->size)
goto out;
if (*f_pos + count >= dev->size )
count = dev->size - *f_pos;
/*在量子集中寻找链表项目、qset索引以及偏移量*/
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; /* don't fill holes*/
/* 读取该量子的数据直到结尾 */
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;
}
*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; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
/* find listitem, qset index and offset in the quantum */
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum; q_pos = rest % quantum;
/* follow the list up to the right position */
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;
}
/* write only up to the end of this quantum */
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;
/* update the size */
if (dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
/* 关联file_operations */
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release
};
static int scull_setup_cdev(struct scull_dev *scull_dev, int index)
{
int err;
dev_t devno = MKDEV(scull_major,scull_minor + index);
// int major = MAJOR(dev), minor = MINOR(dev);
// dev_t devno = MKDEV(major, minor + index); /* 利用已经获得的设备号 */
cdev_init(&scull_dev->cdev, &scull_fops); /*cdev 的初始化*/
scull_dev->cdev.owner = THIS_MODULE;
scull_dev->cdev.ops = &scull_fops;
err = cdev_add(&scull_dev->cdev, devno, 1);
if (err)
{
printk(KERN_NOTICE "Error %d adding scull!", err);
return err;
}
return 0;
} /*scull 设备注册*/
static int __init scull_init(void)
{
int ret = -1;
int i;
printk(info);
/* 动态分配设备号*/
if (scull_major){
dev = MKDEV(scull_major,scull_minor );
ret = register_chrdev_region(dev, scull_nr_devs, "scull");
}
else{
ret = alloc_chrdev_region(&dev, scull_minor,scull_nr_devs,"scull");
scull_major = MAJOR(dev);
}
if (ret < 0){
printk(KERN_WARNING "scull: can't get major %d\n",scull_major);
return ret;
}
else{
printk(DEVICE_NAME "major = %d\n",scull_major);
}
// struct cdev *sdev = cdev_alloc(); /*分配一个独立得cdev结构*/
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
ret = -ENOMEM;
return ret; /* Make this more graceful */
}
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
/* void *memset(void *s,int c,size_t n)总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c */
/* Initialize each device. */
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);
ret = scull_setup_cdev(&scull_devices[i], i);
if (ret < 0){
printk(DEVICE_NAME "can't register major module!\n");
return ret;
}
}
/*注册一个类,使得mdev能在/dev下创建设备节点??????*/
scull_class = class_create(THIS_MODULE,DEVICE_NAME);
if(IS_ERR(scull_class))
{
printk(KERN_ALERT "Err:faile in scull_class!\n");
return -1;
}
/*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/
device_create(scull_class, NULL, dev, NULL, DEVICE_NAME);
printk(DEVICE_NAME"initializa\n");
return 0;
}
static void __exit scull_exit(void)
{
printk(KERN_ALERT "Moudle exit! Charater Device Driver End!\n");
unregister_chrdev_region(dev,scull_nr_devs);
device_destroy(scull_class, dev);
class_destroy(scull_class);
}
module_init(scull_init);
module_exit(scull_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("CHARACTER DEVICE DRVER");
MODULE_AUTHOR("dengwei");
//scull.c
-------------------------------------------------------------------
#define SCULL_MAJOR 0
#define SCULL_MINOR 0
#define SCULL_NR_DEVS 1
#define DEVICE_NAME "scull"
#define SCULL_QUANTUM 4000
#define SCULL_QSET 1000
/* Representation of scull quantum qset */
struct scull_qset{
void **data;
struct scull_qset *next;
};
struct scull_dev{
struct scull_qset *data;
int quantum;
int qset;
unsigned long size;
unsigned int access_key;
struct semaphore sem;
struct cdev cdev;
};
//scull.h
-------------------------------------------------------------
ifneq ($(KERNELRELEASE),)
obj-m :=scull.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm -r -f .tmp_versions *.mod.c .*.cmd *.o Modules.symvers
endif
//Makefile
=============================================================
2.测试程序
#include
#include
#include
#include
#include
int main()
{
char buffer1[20]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
char buffer2[20]={0};
int sculltest;
int code,i;
sculltest = open("/dev/scull",O_WRONLY );
for ( i=20 ; i>0 ; i-=code) {
if ((code=write(sculltest , &buffer1[20-i] , i)) != i) printf("write error! code=%d \n",code);
else printf("write ok! code=%d \n",code);
}
close(sculltest);
sculltest = open("/dev/scull",O_RDONLY );
for ( i=20 ; i>0 ; i-=code) {
if ((code=read(sculltest , &buffer2[20-i] , i)) != i) printf("read error! code=%d \n",code);
else printf("read ok! code=%d \n",code);
}
for(i=0;i<20;i+=5)
printf("[%d]=%d [%d]=%d [%d]=%d [%d]=%d [%d]=%d\n",i,buffer2[i],i+1,buffer2[i+1],i+2,buffer2[i+2],i+3,buffer2[i+3],i+4,buffer2[i+4]);
printf("\n");
close(sculltest);
exit(0);
}
//scull-test.c
-------------------------------------------------------------------
#CROSS_COMPILE =/home/tekkaman/working/crosstool-gcc410-k26222/gcc-4.1.0-glibc-2.3.2/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-
#CC = $(CROSS_COMPILE)gcc
CC = gcc
all : scull_test.o
$(CC) -o scull_test scull_test.o
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
//Makefile
==============================================================
3,result:
root@dw:/home/dengwei/eclipse_workspace/scull_driver_test# cat /proc/devices |grep sc
10 misc
251 scull
//驱动挂载
-------------------------------------------------------------
root@dw:/home/dw/scull-test# ./scull_test
write ok! code=20
read ok! code=20
[0]=0 [1]=1 [2]=2 [3]=3 [4]=4
[5]=5 [6]=6 [7]=7 [8]=8 [9]=9
[10]=10 [11]=11 [12]=12 [13]=13 [14]=14
[15]=15 [16]=16 [17]=17 [18]=18 [19]=19
//测试成功
==========================================================
总结:
1,
struct cdev 和dev_t 的联系。
dev_t:包含设备号,用来表示设备;
cdev:设备的结构
怎样把两者联系起来呢?
通过这个函数:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count)
argument *dev:表示要注册的设备结构;
argument num:表示要注册的设备号;//书上说是设备对应的第一个编号??
argument count:表示应该和该设备关联的设备号的数量。
2,class_create(THIS_MODULE, DEVICE_NAME)
device_create
见下一章(在驱动模块初始化函数中实现设备节点的自动创建)
3,字符设备的执行流程:
获取设备号->注册设备->关联file_operations结构->open->write->read->release资源->close(设备)
=========================================================
参考资料:http://blog.chinaunix.net/u1/34474/showart_404278.html
阅读(1054) | 评论(0) | 转发(0) |