Chinaunix首页 | 论坛 | 博客
  • 博客访问: 376723
  • 博文数量: 57
  • 博客积分: 2299
  • 博客等级: 大尉
  • 技术积分: 1109
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-27 23:12
文章分类
文章存档

2011年(4)

2010年(53)

分类: 嵌入式

2010-02-28 00:52:11

序:在没有拿到标准源码之前,自己写的scull的驱动还是有问题,现在又看了一遍字符设备驱动,正好今天下载到了标准源码,对比自己的看问题就出在4个设备节点的一个设备节点。再结合天嵌手册上驱动的编写方式,二者结合写了scull单一设备的驱动,利用的是标准源码的思想和规范,加上自己的想法。现在更加体会到了书读百遍的妙处。

 

准备过程中发现自己的C确实需要加强

struct scull_dev my_dev;

struct scull_dev *my_dev;

函数调用:int scull_setup_cdev(struct scull_dev *dev)

struct scull_dev *my_dev;定义的就错了

为何困扰我一个多小时?

 

 

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           */

};

 

上述错误找到了,编译没有任何错误,设备生成后不能写数据,经过好长时间的调试,发现,写数据的时候到if (down_interruptible(&dev->sem))       return -ERESTARTSYS;这儿的时候内核就挂了,这个检验不知道是什么功能,不能上网就是这点麻烦,新的错误不能及时上网查找。做完未能解决这个问题,今晚仔细对比Tekkaman Ninja这位大哥的程序才发现初始化的时候dev->quantum = scull_quantum;  dev->qset = scull_qset;init_MUTEX(&dev->sem);3项忘了初始化,错误的原因应该是在于最后一项需要初始化个什么东东,好,把它加入到scull_setup_cdev函数里边。

       具有读写基本功能的scull设备驱动程序如下:

/*

*File Name                          :scull.c

*Function                            :test character device driver, only register one scull

*Author                               :gufeiyang

*From                                        :<>

*Time                                        :2010-2-7 home yunnan

*/

#include                                                /*EXPORT_SYMBOL_GPL ())*/

#include                         /*module_param(variable, type, perm); */

#include                                                      /*module_init(init_function); module_exit(cleanup_function); */

 

#include                                                 /*int printk(const char * fmt, ...);*/

#include                                                  /* size_t */

#include                                                /*dev_t*/

#include                                                              /* everything... */

#include                                                   /*struct cdev *cdev_alloc(void); */

#include          /* kmalloc() */

#include                                                 /*struct class*/

#include              /* cli(), *_flags */

#include      /* copy_*_user */

#include "scull.h"

 

 

static int scull_major =   SCULL_MAJOR;

static int scull_minor =   0;

static int scull_nr_devs = SCULL_NR_DEVS;     /* number of bare scull devices */

static int scull_quantum = SCULL_QUANTUM;

static int scull_qset =    SCULL_QSET;

 

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 my_dev;

static struct class *scull_class;

 

static 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;

}

static 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 0

       printk(KERN_WARNING "\nquantum = %d qset = %d\n", quantum, qset);

       printk(KERN_WARNING"write sem = %d\n",(int)&dev->sem);

       printk(KERN_WARNING"write cdev = %d\n",(int)&dev->cdev);

#endif

       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;

}

static 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; /* the first listitem */

       int quantum = dev->quantum, qset = dev->qset;

       int itemsize = quantum * qset; /* how many bytes in the listitem */

       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;

 

       /* 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 = scull_follow(dev, item);

       if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])

              goto out; /* don't fill holes */

 

       /* read only up to the end of this quantum */

       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;

}

static int scull_release(struct inode *inode, struct file *filp)

{

       printk(KERN_WARNING"\nModule Released![kernel]\n");

       return 0;

}

static int scull_trim(struct scull_dev *dev)

{

       struct scull_qset *next, *dptr;

       int qset = dev->qset; /* "dev" is not-null */

       int i;

       for (dptr = dev->data; dptr; dptr = next)

       { /* all the list items */

              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 int scull_open(struct inode *inode, struct file *filp)

{

       struct scull_dev *dev; /* device information */

       printk(KERN_WARNING"Module Opened![kernel]\n");

       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); /* ignore errors */

       }

//     printk(KERN_WARNING"open sem = %d\n",(int)&dev->sem);

      

       return 0; /* success */

}

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

};

/*

 * Set up the char_dev structure for this device.

 */

static int scull_setup_cdev(struct scull_dev *dev)

{

       int err = 1, devno = MKDEV(scull_major, scull_minor );

 

       /*init data*/

       dev->quantum = scull_quantum;

       dev->qset = scull_qset;

       init_MUTEX(&dev->sem);

      

       /*register*/

       cdev_init(&dev->cdev, &scull_fops);

       dev->cdev.owner = THIS_MODULE;

       dev->cdev.ops = &scull_fops;

       err = cdev_add (&dev->cdev, devno, 1);

       /* Fail gracefully if need be */

       if (err)

       {

              printk(KERN_NOTICE "Error %d adding scullc\n", err);

              return err;

       }

       return 0;//ok

}

static int __init scull_init(void)

{

       int result,ret;

       dev_t dev;

/*

       * Get a range of minor numbers to work with, asking for a dynamic

       * major unless directed otherwise at load time.

 */

       if (scull_major) {

              dev = MKDEV(scull_major, scull_minor);

              result = register_chrdev_region(dev, scull_nr_devs, DEV_NAME);

       } else {

              result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, DEV_NAME);

              scull_major = MAJOR(dev);

       }    

       if (result < 0) {

              printk(KERN_WARNING "scull: can't get major %d\n", scull_major);

              return result;

       }else {

              printk(KERN_WARNING"Scull_major = %d\n", scull_major);

       }

       /*注册设备*/

//     ret = register_chrdev(scull_major, DEV_NAME, &scull_fops);//老方法

       ret = scull_setup_cdev(&my_dev);//新方法

       if (ret < 0)

       {

              printk(KERN_WARNING " can't register major number\n");

              return ret;

       }

       //printk(KERN_WARNING"init sem = %d\n",(int)&my_dev.sem);

       /*注册一个类,使mdev可以在/dev下创建设备节点*/

       scull_class = class_create(THIS_MODULE, DEV_NAME);

       if(IS_ERR(scull_class))

       {

              printk(KERN_ALERT"Err:faile in scull_class!\n");

              return -1;

       }

       /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/

       class_device_create(scull_class, NULL, dev, NULL, DEV_NAME);

       printk(KERN_WARNING"Module Initialed!\nChracter Device Driver Start!\n");

       return 0;

}

static void __exit scull_exit(void)

{

       int devno = MKDEV(scull_major, scull_minor );

//     unregister_chrdev_region(devno, scull_nr_devs);

       cdev_del(&my_dev.cdev);

       class_device_destroy(scull_class, devno);

       class_destroy(scull_class);

       printk(KERN_ALERT"Module exit!\nChracter Device Driver End!\n");

}

module_init(scull_init);

module_exit(scull_exit);

MODULE_LICENSE("Dual BSD/GPL");

MODULE_DESCRIPTION("CHARACTER DEVICE DRVER");

MODULE_AUTHOR("gufeiyang@2010-02-07");

/*

*File Name : scull.h

*/

#ifndef    _SCULL_H_

#define    _SCULL_H_

 

#define    DEV_NAME   "scull"

#ifndef SCULL_MAJOR

#define SCULL_MAJOR 0   /* dynamic major by default */

#endif

 

#ifndef SCULL_NR_DEVS

#define SCULL_NR_DEVS 1   /* scull */

#endif

 

/*

 * The bare device is a variable-length region of memory.

 * Use a linked list of indirect blocks.

 *

 * "scull_dev->data" points to an array of pointers, each

 * pointer refers to a memory area of SCULL_QUANTUM bytes.

 *

 * The array (quantum-set) is SCULL_QSET long.

 */

#ifndef SCULL_QUANTUM

#define SCULL_QUANTUM 4000

#endif

 

#ifndef SCULL_QSET

#define SCULL_QSET    1000

#endif

 

/*

 * Representation of scull quantum sets.

 */

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           */

};

#endif

这个程序由Tekkaman Ninja和《linux设备驱动程序(第三版)》二者结合写成,参考了Tekkaman Ninja大哥的结构思路,有进步的地方就是不需要创建节点mknod这一步骤,实际当中也不会让你去那么做的,而是在驱动程序中直接生成节点,这一功能的完成参照天嵌手册上的驱动教程修改而实现。还有Tekkaman Ninja大哥估计是没注意,不知道他是如何编译成功的,在定义*scullc_cache;的时候他包里的源码是“kmem_cache_t *scullc_cache;”这样在2.6.25.内核中不能编译,需要修改成“struct kmem_cache *scullc_cache;”,编译不过去我通过在内核目录中grep –rn ‘kmem_cache’ ./*  才发现原来kmem_cache 是一个结构,并且是kmem_cache,不知道是不是写错了,当然,我没有用他那种先总体分配内存单元,然后再“切割”成scull_dev大小,而是参考天嵌的用“类”的思想,先申请了scull_dev的空间,然后再生成设备节点。当然,我只生成了一个设备,Tekkaman Ninja分配了4个设备的空间,需要脚本生成4个设备节点,我先用一个做测试,多的设备需要重新修改代码。

       Tekkaman Ninja大哥相比,晚辈显得愚钝……!

测试结果程序,程序用的前辈的:

[root@gfy-S3C2440 /tmp]# ./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

达到预期效果也为下一步铺了道路。

自己写了个应用程序,新手,呵呵  有些笨拙,没有前辈的思想:

#include

#include

#include

#include

#include

#include

int main()

{

       int fd,i;

       char buf[]="I Love Linux!";

       char buf2[20]= {0};

       if((fd = open("/dev/scull",O_WRONLY)) == -1)

       {

              perror("Open");

              return -1;

       } else {

              printf("File Opened!\n");

       }

       for( i = 0; buf[i]  != NULL;i++  ){ //此法愚钝,一个一个字节写,还是前辈的有思想,我是新手,先练练

              if(write(fd,&buf[i],1) == -1)

              {

                     perror("Write:");

                     printf("Write Error!buf[%d] = %c\n",i,buf[i]);

              }

              else

                     printf("Write OK!buf[%2d] = %c\n",i,buf[i]);

       }

       printf("\nReading......\n");

       sleep(1); //防止release模块比打印终端快,调试信息有插入

       close(fd);

       if((fd = open("/dev/scull",O_RDONLY)) == -1)

       {

              perror("Open");

              return -1;

       } else

              printf("File Opened!\n");

       for( i = 0; i < strlen(buf);i++  ){ //此法愚钝,一个一个字节写,还是前辈的有思想,我是新手,先练练

              if(read(fd,&buf2[i],1) == -1)

                     printf("Read Error!buf2[%d] = %c\n",i,buf[i]);

              else

                     printf("Read OK!buf2[%2d] = %c\n",i,buf[i]);

       }

       sleep(1); //防止release模块比打印终端快,调试信息有插入

       close(fd);

       return 0;

}测试结果:

[root@gfy-S3C2440 /tmp]# ./scull-test

Module Opened![kernel]

File Opened!

Write OK!buf[ 0] = I

Write OK!buf[ 1] =

Write OK!buf[ 2] = L

Write OK!buf[ 3] = o

Write OK!buf[ 4] = v

Write OK!buf[ 5] = e

Write OK!buf[ 6] =

Write OK!buf[ 7] = L

Write OK!buf[ 8] = i

Write OK!buf[ 9] = n

Write OK!buf[10] = u

Write OK!buf[11] = x

Write OK!buf[12] = !

Read……!

Module Released![kernel]

Module Opened![kernel]

File Opened!

Read OK!buf2[ 0] = I

Read OK!buf2[ 1] =

Read OK!buf2[ 2] = L

Read OK!buf2[ 3] = o

Read OK!buf2[ 4] = v

Read OK!buf2[ 5] = e

Read OK!buf2[ 6] =

Read OK!buf2[ 7] = L

Read OK!buf2[ 8] = i

Read OK!buf2[ 9] = n

Read OK!buf2[10] = u

Read OK!buf2[11] = x

Read OK!buf2[12] = !

Module Released![kernel]

[root@gfy-S3C2440 /tmp]# cat /dev/scull

Module Opened![kernel]

I Love Linux!

Module Released![kernel]

2010-2-10

今天晚上发现个问题,在上述驱动中,请看卸载模块函数:

static void __exit scull_exit(void)

{

       int devno = MKDEV(scull_major, scull_minor );

//     unregister_chrdev_region(devno, scull_nr_devs);

       cdev_del(&my_dev.cdev);

       class_device_destroy(scull_class, devno);

       class_destroy(scull_class);

       printk(KERN_WARNING"Module exit!\nChracter Device Driver End!\n");

}

用的是cdev_del(&my_dev.cdev);移除设备,《LDD3》上所说当cdev(字符设备结构)结构传递给该函数后就再也不能访问该结构了,说明该函数的功能只是释放cdev结构的空间,我发现rmmod后在/proc/devices还存在该设备的名字和主设备号,并且反复卸载和注册后系统的主设备号会被用尽,如下

204 tq2440_serial

250 scull

251 scull

252 scull

253 usb_endpoint

所以正确的卸载模块函数是:

static void __exit scull_exit(void)

{

       int devno = MKDEV(scull_major, scull_minor );

       unregister_chrdev_region(devno, scull_nr_devs);

       cdev_del(&my_dev.cdev);

       class_device_destroy(scull_class, devno);

       class_destroy(scull_class);

       printk(KERN_WARNING"Module exit!\nChracter Device Driver End!\n");

}

unregister_chrdev_region(devno, scull_nr_devs);函数是早期设备注册所用的反注册方法,不知道为什么用新方法注册的设备也可以用老的卸载方法下载,并且连同主设备号一起也给销毁了,这样就可以在反复卸载与注册模块的时候用的还是一个主设备号,不会将主设备号耗尽,也同时释放了cdev结构的空间,就不知道unregister_chrdev_regio()会不会释放空间了,我猜不会,否则报错了。这个只是实验经验,理论问题还得看内核源码……路漫漫呀!

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