Chinaunix首页 | 论坛 | 博客
  • 博客访问: 618347
  • 博文数量: 140
  • 博客积分: 2635
  • 博客等级: 少校
  • 技术积分: 1353
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-04 15:46
文章分类
文章存档

2015年(2)

2014年(12)

2013年(10)

2012年(10)

2011年(85)

2010年(21)

分类: LINUX

2013-09-24 12:52:04

通过两天的努力把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

阅读(708) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~