通过两天的努力把linux下字符型设备实现。开发过程中,学过的文件系统、竞争、字符型设备、用户空间和内核空间数据传输都是零散的知识。通过手动编写代码让我清晰的明白了很多知识的运用。
整个程序的步骤LDD书中讲的很清晰,我在稍微总结一翻。
1.创建你要的设备类型,次设备类型如同你要操作的对象,里面每个字段都是你要使用和调用的方法。
2.主设备号表示表示你编写设备驱动,次设备号表示使用这个驱动程序的设备是唯一的。
dev_t类型32bit,dev_t(int major,int minor); #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) 主设备12bit,次设备20bit。MINORBITS = 20
主次设备号注册:register_chrdev_region 和alloc_chrdev_region 分配给devt *dev。unregister_chrdev_region释放设备号。
linux下需要对应的设备节点和文件系统相关连,这样可以提供对应的操作服务。驱动操作需要file_operations,file,inode这三个数据结构。file_operations 如同对象,
对象中包含了对应的方法。file是代表打开的文件,inode是内部表示文件,inode只想一个字符设备文件,该字段包含了 struct cdev。
3.字符设备初始化:cdev_init, 设置完成后,cdev_add 告知该结构的信息给内核。
4.对应的方法可以针对性的去操作。
#include
#include
#include
#include /* printk() */
#include /* kmalloc() */
#include /* everything... */
#include /* error codes */
#include /* size_t */
#include
#include /* O_ACCMODE */
#include
#include
#include
MODULE_AUTHOR("zhanbing.li@cs2c.com.cn");
MODULE_LICENSE("GPL");
struct cs2c_dev {
void **data;
struct cs2c_dev *next; /* next listitem */
int vmas; /* active mappings */
int quantum; /* the current allocation size */
int qset; /* the current array size */
size_t size; /* 32-bit will suffice */
struct semaphore sem; /* Mutual exclusion */
struct cdev cdev;
};
#define CS2C_QUANTUM 4000 /* use a quantum size like scull */
#define CS2C_QSET 500
int cs2c_qset = CS2C_QSET;
int cs2c_quantum = CS2C_QUANTUM; /* use a quantum size like scull */
struct cs2c_dev *cs2c_devices;
static int cs2c_major;
int cs2c_trim(struct cs2c_dev *dev);
/* declare one cache pointer: use it for all devices */
struct kmem_cache *cs2c_cache;
static int cs2c_release(struct inode *inode, struct file *filp)
{
// struct _dev *deva; /*device information*/
//deva = container_of(inode->i_cdev,struct ctest_dev,ctest);
// filp->private_data = deva; /*for other emthods*/
/* now trim to 0 the lenght of device if open was write-only */
printk("Is ctest_release function.\n");
return 0;
}
int cs2c_open(struct inode *inode, struct file *filp){
struct cs2c_dev *dev;
dev = container_of(inode->i_cdev,struct cs2c_dev,cdev);
filp-> private_data =dev;
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible (&dev->sem))
return -ERESTARTSYS;
cs2c_trim(dev); /* ignore errors */
up (&dev->sem);
}
printk("Is ctest_open function.\n");
return 0;
}
int cs2c_trim( struct cs2c_dev *dev)
{
struct cs2c_dev *dptr,*next;
int qset = dev->qset;
int i;
for (dptr = dev;dptr; dptr = next){
if (dptr->data) {
for (i=0; i
if (dptr->data[i])
kmem_cache_free(cs2c_cache, dptr->data[i]);
kfree(dptr->data);
dptr->data=NULL;
}
next = dptr->next;
if (dptr != dev) kfree(dptr);
}
dev->size = 0;
dev->qset = cs2c_qset;
dev->quantum = cs2c_quantum;
dev->next = NULL;
return 0;
}
/*
* Follow the list
*/
struct cs2c_dev *cs2c_follow(struct cs2c_dev *dev, int n)
{
while (n--) {
if (!dev->next) {
dev->next = kmalloc(sizeof(struct cs2c_dev), GFP_KERNEL);
memset(dev->next, 0, sizeof(struct cs2c_dev));
}
dev = dev->next;
continue;
}
return dev;
}
ssize_t cs2c_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos){
return 0;
struct cs2c_dev *dev = filp->private_data;
struct cs2c_dev *dptr;
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = 0;
printk("Is cs2c_read function\n");
if (down_interruptible (&dev->sem))
return -ERESTARTSYS;
printk("down_interruptable function.\n");
if (*f_pos > dev->size)
goto nothing;
if (*f_pos + count > dev->size)
count = dev->size - *f_pos;
/* 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 (defined elsewhere) */
dptr = cs2c_follow(dev, item);
if (!dptr->data)
goto nothing; /* don't fill holes */
if (!dptr->data[s_pos])
goto nothing;
if (count > quantum - q_pos)
count = quantum - q_pos; /* read only up to the end of this quantum */
if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) {
retval = -EFAULT;
goto nothing;
}
up (&dev->sem);
*f_pos += count;
printk("Is end cs2c_read function.\n");
return count;
nothing:
up (&dev->sem);
printk("Is Nothing cs2c_read function.\n");
return retval;
}
ssize_t cs2c_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
struct cs2c_dev *dev = filp->private_data;
struct cs2c_dev *dptr;
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM; /* our most likely error */
printk ("Is cs2c_wirte function.\n");
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 = cs2c_follow(dev, item);
if (!dptr->data) {
dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
if (!dptr->data)
goto nomem;
memset(dptr->data, 0, qset * sizeof(char *));
}
if (count > quantum - q_pos)
count = quantum - q_pos; /* write only up to the end of this quantum */
if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {
retval = -EFAULT;
goto nomem;
}
*f_pos += count;
/* update the size */
if (dev->size < *f_pos)
dev->size = *f_pos;
up (&dev->sem);
printk ("end cs2c_write OK\n");
return count;
nomem:
printk ("fail cs2c_write bak\n");
up (&dev->sem);
return retval;
}
struct file_operations cs2c_fops = {
.owner = THIS_MODULE,
.open = cs2c_open,
.release = cs2c_release,
// .llseek = scullc_llseek,
.read = cs2c_read,
.write = cs2c_write,
// .unlocked_ioctl = scullc_ioctl,
// .aio_read = scullc_aio_read,
// .aio_write = scullc_aio_write,
};
int cs2c_init(void)
{
dev_t dev;
int result;
result = alloc_chrdev_region(&dev,0,1,"cs2c_device");
if (result < 0) {
printk("alloc fail\n");
return result;
}
cs2c_major= MAJOR(dev);
printk("register char device number successfully,major=%d,minor=%d\n",MAJOR(dev),MINOR(dev));
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
cs2c_devices = kmalloc(sizeof (struct cs2c_dev), GFP_KERNEL);
if (!cs2c_devices) {
result = -ENOMEM;
return -1;
}
memset(cs2c_devices, 0, sizeof (struct cs2c_dev));
cs2c_devices->quantum = cs2c_quantum;
cs2c_devices->qset = cs2c_qset;
sema_init (&cs2c_devices->sem, 1);
cdev_init(&cs2c_devices->cdev,&cs2c_fops);
result = cdev_add(&cs2c_devices->cdev,dev,1);
if (result)
printk(KERN_NOTICE "Error %d adding cs2c 1",result);
printk ("initiliza OK\n");
return 0;
}
void cs2c_exit(void){
cdev_del(&cs2c_devices->cdev);
kfree(cs2c_devices);
unregister_chrdev_region(MKDEV(cs2c_major,0),1);
}
module_init(cs2c_init);
module_exit(cs2c_exit);
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m := cs2c.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -f *.ko *.mod.c *.mod.o *.o
阅读(746) | 评论(0) | 转发(0) |