设备驱动之一 - 基于第三章的组织结构改变的scull驱动
LDD3到第三章的组织结构改变的scull驱动
scull.h文件---驱动头文件
/**********************************************
* Author: lewiyon@hotmail.com
* File name: scull.h
* Description: define scull
* Date: 2012-07-4
*********************************************/
#ifndef __SCULL_H
#define __SCULL_H
#include <linux/semaphore.h>
#include <linux/cdev.h>
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0
#endif
#ifndef SCULL_NR_DEVS
#define SCULL_NR_DEVS 1
#endif
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4096
#endif
#ifndef SCULL_QSET
#define SCULL_QSET 4096
#endif
/*
* parameters of module
*/
extern int scull_major;
extern int scull_quantum;
extern int scull_qset;
struct scull_qset {
void **data;
struct scull_qset *next;
};
/*
* define scull_dev struct
*/
struct scull_dev {
struct scull_qset *data; /* point first child set */
int quantum; /* size of current set */
int qset; /* size of array */
unsigned long size; /* save total quantity */
unsigned int access_key; /* used by scullid and scullpriv */
struct semaphore sem; /* mutex */
struct cdev cdev; /* character device */
};
extern int scull_trim(struct scull_dev *dev);
#endif
main.c -- 驱动模块实现源码
/**********************************************
* Author: lewiyon@hotmail.com
* File name: scullmod.c
* Description: initialize and release function for scull
* Date: 2012-07-4
*********************************************/
#include <linux/init.h> /* module */
#include <linux/module.h> /* module */
#include <linux/moduleparam.h> /* module */
#include <linux/errno.h> /* error codes */
#include <linux/kernel.h> /* printk */
#include <linux/slab.h> /* kmalloc kfree */
#include <linux/types.h> /* dev_t */
/* local head files */
#include "scull.h"
#include "file.h"
/* default parameters of module */
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;
/* input parameters of module */
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);
struct scull_dev *scull_devices;
/*
* scull_trim - 遍历链表,并释放所有找到的量子和量子集
* @dev: scull设备
*/
int scull_trim(struct scull_dev *dev)
{
int i, qset;
struct scull_qset *next, *dptr;
qset = dev->qset;
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;
}
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err;
int devno;
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);
}
/*
* initialze scull module
*/
void scull_cleanup_module(void)
{
int i;
dev_t devno;
devno = MKDEV(scull_major, scull_minor);
/* delete each entry */
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);
}
/* unregister */
unregister_chrdev_region(devno, scull_nr_devs);
printk(KERN_WARNING "scull: Bye!\n");
}
/*
* initialze scull module
*/
int scull_init_module(void)
{
int i, res;
dev_t dev = 0;
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
res = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
res = alloc_chrdev_region(&dev, scull_minor,
scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if (res < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return res;
}
/*
* allocate the device struct cache
*/
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev),
GFP_KERNEL);
if (NULL == scull_devices) {
res = -ENOMEM;
printk(KERN_WARNING "scull: NOMEM for scull!\n");
goto fail;
}
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
/* initialize each device */
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
sema_init(&scull_devices[i].sem, 1);
scull_setup_cdev(&scull_devices[i], i);
}
printk(KERN_INFO "scull: OK!\n");
return 0;
fail:
scull_cleanup_module();
return res;
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
MODULE_AUTHOR("lewiyon@hotmail.com");
MODULE_LICENSE("GPL");
file.h -- 文件操作函数头文件
/********************************************** * Author: lewiyon@hotmail.com * File name: file.h * Description: file header * Date: 2012-07-4 *********************************************/ #ifndef __SCULL_FILE_H #define __SCULL_FILE_H extern const struct file_operations scull_fops; #endif
file.c -- 文件操作函数源码
/**********************************************
* Author: lewiyon@hotmail.com
* File name: file.c
* Description: realize cull file ops
* Date: 2012-07-04
*********************************************/
#include <linux/module.h> /* THIS_MODULE */
#include <linux/kernel.h> /* printk & container */
#include <linux/uaccess.h> /* cp_to/from_user */
#include <linux/types.h> /* size_t */
#include <linux/fs.h> /* inode st. */
#include "scull.h"
/*
* scull_follow - 在指定的scull_dev中查找指定的量子集
* @dev: scull_dev设备结构体指针
* @n: 量子集在scull_dev中的位置
*
* return:
* @对应量子集结构指针 - 成功;
* @NULL - 失败;
*/
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
struct scull_qset *qs;
qs = dev->data;
if (!qs)
{
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
{
printk(KERN_WARNING "scull_follow_if_fail\n");
return NULL;
}
memset(qs, 0, sizeof(struct scull_qset));
}
/* 创建其他的量子集 */
while (n--)
{
if (!qs->next)
{
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (NULL == qs->next)
{
printk(KERN_WARNING "scull_follow_n_%d\n", n);
return NULL;
}
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
}
return qs;
}
/*
* scull_read - 从scull_dev中的文件读取数据
*/
ssize_t scull_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct scull_dev *dev;
struct scull_qset *dptr;
int quantum, qset, itemsize;
int item, rest, s_pos, q_pos;
ssize_t retval;
dev = filp->private_data;
quantum = dev->quantum;
qset = dev->qset;
itemsize = quantum * qset;
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;
/* 查找listitem中量子集的索引以及量子偏移位 */
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
/* 在dev中查找量子集item */
dptr = scull_follow(dev, item);
if (NULL == dptr || !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;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
/*
* scull_write - 往scull_dev中的文件写入数据
*/
ssize_t scull_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct scull_dev *dev;
struct scull_qset *dptr;
int quantum , qset;
int itemsize, item, s_pos, q_pos, rest;
ssize_t retval;
dev = filp->private_data;
quantum = dev->quantum;
qset = dev->qset;
itemsize = quantum * qset;
retval = -ENOMEM;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
/* 查找listitem中量子集的索引以及量子偏移位 */
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
/* 在dev中查找量子集item */
dptr = scull_follow(dev, item);
if (dptr == NULL)
{
printk(KERN_WARNING "scull_follow_fail\n");
goto out;
}
if (!dptr->data)
{
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
{
printk(KERN_WARNING "km_dptr->data_fail\n");
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])
{
printk(KERN_WARNING "km_dptr->data[s_pos]_fail\n");
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;
printk(KERN_WARNING "copy_fail\n");
goto out;
}
*f_pos += count;
retval = count;
/* 更新scull_dev数据大小记录 */
if (dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
/*
* scull_llseek - 往scull_dev中的文件写入数据
*/
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
struct scull_dev *dev;
loff_t newpos;
dev = filp->private_data;
switch(whence)
{
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case SEEK_END:
newpos = dev->size + off;
break;
default:
return -EINVAL;
}
if (newpos < 0)
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
/*
* scull_open - 打开scull_dev中的文件
*/
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 (O_WRONLY == (filp->f_flags & O_ACCMODE))
{
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
scull_trim(dev);
up(&dev->sem);
}
return 0;
}
/*
* scull_release - 关闭scull_dev中的文件
*/
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
const 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,
};
makefile文件
obj-m += scull.o scull-objs := main.o file.o KERNELBUILD := /lib/modules/$(shell uname -r)/build default: $(MAKE) -C $(KERNELBUILD) M=$(shell pwd) modules clean: -rm -rf *.o .*.cmd *.ko* *.mod.c .tmp_versions -rm -rf modules.order Module.symvers
测试文件:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int fp0;
char Buf[4096];
/* 初始化Buf */
strcpy(Buf,"Scull is char dev!");
printf("BUF: %s\n",Buf);
/* 打开设备文件 */
fp0 = open("/dev/scull0", O_RDWR);
if (fp0 < 0)
{
printf("Open scull Error!\n");
return -1;
}
/* 写入设备 */
strcpy(Buf,"Scull write!");
write(fp0, Buf, sizeof(Buf));
/* 重新定位文件位置 */
lseek(fp0,0,SEEK_SET);
/* 清除Buf */
strcpy(Buf,"Buf is NULL!");
/* 读出设备 */
read(fp0, Buf, sizeof(Buf));
/* 检测结果 */
printf("BUF: %s success\n",Buf);
return 0;
}
参考文献:(部分代码摘自下列网站)
http://www.cnblogs.com/adolph-suyu/archive/2011/12/04/2275990.html
posted on 2012-07-11 19:45 YoungerChina 阅读(371) 评论(0) 收藏 举报
浙公网安备 33010602011771号