linux设备驱动(24)字符设备

1 字符设备的实例

以led驱动的简单字符设备驱动

led_driver.c

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <asm/uaccess.h>
  7 #include <asm/irq.h>
  8 #include <asm/io.h>
  9 #include <asm/arch/regs-gpio.h>
 10 #include <asm/hardware.h>
 11 #include <linux/delay.h>
 12 
 13 static struct class *led_drv_class;
 14 static struct class_device    *led_drv_class_dev;
 15 
 16 volatile unsigned long *gpfcon = NULL;
 17 volatile unsigned long *gpfdat = NULL;
 18 
 19 //#define FIRST_DRV_MAJOR 111
 20 //#define DEVICE_NAME first_drv 
 21 
 22 static int led_drv_open(struct inode *inode, struct file *file)
 23 {
 24     //printk("led_drv_open test!!!\n");
 25     *gpfcon &= ~((0x3<<(4*2))|(0x3<<(5*2))|(0x03<<(6*2)));//先清零
 26     *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));//置1,设置GPIOE4/5/6为输出
 27 
 28     return 0;
 29 }
 30 
 31 //static int led_drv_write(struct inode*inode,struct file*file){
 32 static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
 33 {
 34     //printk("led_drv_write test!!!\n");
 35     int val = 0;
 36     copy_from_user(&val,buf,count);
 37     if(val == 1)
 38         {
 39             int i = 5;
 40             while(i)
 41             {
 42              printk("test one 42");
 43              //*gpfdat &= ~((1<<4)|(1<<5)|(1<<6));
 44              
 45              *gpfdat &= ~(1<<4);//led4 on
 46              *gpfdat |= ((1<<5)|(1<<6));//led5,led6 off
 47               msleep(500);
 48              
 49              *gpfdat &= ~(1<<5);
 50              *gpfdat |= ((1<<4)|(1<<6));
 51               msleep(500);
 52               
 53              *gpfdat &= ~(1<<6);
 54              *gpfdat |= ((1<<4)|(1<<5));
 55               msleep(500);
 56              
 57               i = i - 1;
 58            }
 59       }
 60         else
 61         {
 62           printk("test two 62");
 63          *gpfdat |= ((1<<4)|(1<<5)|(1<<6));
 64       }
 65     return 0;
 66 }
 67 
 68 static struct file_operations led_drv_fops = {
 69     .owner = THIS_MODULE,
 70     .open = led_drv_open,
 71     .write = led_drv_write,
 72 };
 73 
 74 int major;
 75 /*
 76 驱动的入口函数
 77 */
 78 static int led_drv_init(void){
 79     
 80     //int major;第一个参数主设备号为0,表示系统会自动分配,还可以自己指定
 81     major = register_chrdev(0,"led_drv",&led_drv_fops);
 82     /*
 83     if(ret<0){
 84         printk(DEVICE "can not register major number");
 85         return ret;
 86     }
 87     */
 88     /*
 89     自动为主设备号为major的字符设备创建dev/xyz
 90     */
 91     led_drv_class = class_create(THIS_MODULE, "led_drv");
 92     led_drv_class_dev = class_device_create(led_drv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
 93     
 94     gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
 95     //*gpfdat = (volatile unsigned long *)ioremap(0x56000044,16);
 96     gpfdat = gpfcon + 1;
 97     return 0;
 98 }
 99 
100 static void led_drv_exit(void){
101     
102     unregister_chrdev(major,"led_drv");
103     /*
104     自动卸载主设备号为major的字符设备dev/xyz
105     */
106     class_device_unregister(led_drv_class_dev);
107     class_destroy(led_drv_class);
108     iounmap(gpfcon);
109 }
110 
111 /*
112 封装驱动入口函数的宏,统一接口,方便内核调用
113 */
114 module_init(led_drv_init);
115 module_exit(led_drv_exit);
116 
117 MODULE_AUTHOR("action_er");
118 MODULE_VERSION("v1.0");
119 //MODULE_DISCRIPTION("leddrivertest");
120 MODULE_LICENSE("GPL");

led_test.c

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <stdio.h>
 5 
 6 int main(int argc,char**argv){
 7     
 8     int fd;
 9     int val = 1;
10     fd = open("dev/xyz",O_RDWR);
11     if(fd<0){
12         printf("hello lhs world!\n");
13         printf("can not open !\n");
14         }
15         
16   if(argc != 2){
17       printf("Usage:\n");
18       printf("%s <on/off>\n",argv[0]);
19       
20       return 0;
21   }
22   if(strcmp(argv[1],"on") == 0)
23       {
24       val = 1;
25     }
26   else
27       {
28           val = 0;
29       }
30     write(fd,&val,4);
31     return 0;
32 }

2 分析字符设备注册过程

函数register_chrdev,定义位于:include\linux\fs.h和fs\char_dev.c中

 1 static inline int register_chrdev(unsigned int major, const char *name,
 2                   const struct file_operations *fops)
 3 {
 4     return __register_chrdev(major, 0, 256, name, fops);
 5 }
 6 
 7 int __register_chrdev(unsigned int major, unsigned int baseminor,
 8               unsigned int count, const char *name,
 9               const struct file_operations *fops)
10 {
11     struct char_device_struct *cd;
12     struct cdev *cdev;
13     int err = -ENOMEM;
14 
15     cd = __register_chrdev_region(major, baseminor, count, name);//根据major注册字符设备
16     if (IS_ERR(cd))
17         return PTR_ERR(cd);
18 
19     cdev = cdev_alloc();
20     if (!cdev)
21         goto out2;
22 
23     cdev->owner = fops->owner;
24     cdev->ops = fops;
25     kobject_set_name(&cdev->kobj, "%s", name);//确定字符设备的名字
26 
27     err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);//向系统中添加字符设备
28     if (err)
29         goto out;
30 
31     cd->cdev = cdev;
32 
33     return major ? 0 : cd->major;
34 out:
35     kobject_put(&cdev->kobj);
36 out2:
37     kfree(__unregister_chrdev_region(cd->major, baseminor, count));
38     return err;
39 }

2.1 函数__register_chrdev_region

作用:

(1)确定字符设备的主设备号

(2)将此字符设备加入全局变量字符设备链表中

 1 static struct char_device_struct *
 2 __register_chrdev_region(unsigned int major, unsigned int baseminor,
 3                int minorct, const char *name)
 4 {
 5     struct char_device_struct *cd, **cp;
 6     int ret = 0;
 7     int i;
 8 
 9     cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);//分配字符设备结构,实际也是一个链表
10     if (cd == NULL)
11         return ERR_PTR(-ENOMEM);
12 
13     mutex_lock(&chrdevs_lock);
14 
15     /* temporary */
16     if (major == 0) {//如果major为0,就会自动分配主设备;如果大于0,就以此值为主设备号
17         for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
18             if (chrdevs[i] == NULL)
19                 break;
20         }
21 
22         if (i == 0) {
23             ret = -EBUSY;
24             goto out;
25         }
26         major = i;//拿到自动分配的主设备号
27         ret = major;
28     }
29     /*给字符设备结构体赋值*/
30     cd->major = major;
31     cd->baseminor = baseminor;
32     cd->minorct = minorct;
33     strlcpy(cd->name, name, sizeof(cd->name));
34 
35     i = major_to_index(major);
36 
37     for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)//将新的字符设备cd加入字符设备结构体数组chrdevs链表中
38         if ((*cp)->major > major ||
39             ((*cp)->major == major &&
40              (((*cp)->baseminor >= baseminor) ||
41               ((*cp)->baseminor + (*cp)->minorct > baseminor))))
42             break;
43 
44     /* Check for overlapping minor ranges.  */
45     if (*cp && (*cp)->major == major) {
46         int old_min = (*cp)->baseminor;
47         int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
48         int new_min = baseminor;
49         int new_max = baseminor + minorct - 1;
50 
51         /* New driver overlaps from the left.  */
52         if (new_max >= old_min && new_max <= old_max) {
53             ret = -EBUSY;
54             goto out;
55         }
56 
57         /* New driver overlaps from the right.  */
58         if (new_min <= old_max && new_min >= old_min) {
59             ret = -EBUSY;
60             goto out;
61         }
62     }
63 
64     cd->next = *cp;
65     *cp = cd;
66     mutex_unlock(&chrdevs_lock);
67     return cd;
68 out:
69     mutex_unlock(&chrdevs_lock);
70     kfree(cd);
71     return ERR_PTR(ret);
72 }

 字符设备定义如下:

1 static struct char_device_struct {
2     struct char_device_struct *next;
3     unsigned int major;
4     unsigned int baseminor;
5     int minorct;
6     char name[64];
7     struct cdev *cdev;        /* will die */
8 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

2.2 函数cdev_add

 1 int cdev_add(struct cdev *p, dev_t dev, unsigned count)
 2 {
 3     int error;
 4 
 5     p->dev = dev;
 6     p->count = count;
 7 
 8     error = kobj_map(cdev_map, dev, count, NULL,//cdev_map是一个全局的kobj_map变量
 9              exact_match, exact_lock, p);
10     if (error)
11         return error;
12 
13     kobject_get(p->kobj.parent);
14 
15     return 0;
16 }

定义全局的kobj_map变量的定义和初始化

drivers\base\map.c和fs\char_dev.c中

 1 static struct kobj_map *cdev_map;
 2 
 3 struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
 4  {
 5      /*分配一个struct kobj_map内存空间 */
 6      struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
 7      /*分配一个struct probe指针内存空间 */
 8      struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
 9      int i;
10 
11      /*分配失败 */
12      if ((p == NULL) || (base == NULL)) {
13          /*释放内存空间 */
14          kfree(p);
15          kfree(base);
16          return NULL;
17      }
18      /*设置默认设备号为1,连续range个次设备,设置probe的实现
19            * 函数(回调)
20            */
21      base->dev = 1;
22      base->range = ~0;
23      base->get = base_probe;
24      /*设置probes数组的初始值 */
25      for (i = 0; i < 255; i++)
26          p->probes[i] = base;
27      p->lock = lock;
28      return p;
29 }

chrdev_init调用函数kobj_map_init初始化cdev_map 

1 void __init chrdev_init(void)
2 {
3     cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
4     bdi_init(&directly_mappable_cdev_bdi);
5 }

2.3 函数 kobj_map

定义位于:drivers\base\map.c

 1 int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
 2           struct module *module, kobj_probe_t *probe,
 3           int (*lock)(dev_t, void *), void *data)
 4  {
 5      /*计算MAJOR(dev)到MAJOR(dev + range - 1)有几个
 6       * 主设备,由于主设备号都一样,所以这里n = 1
 7            */
 8      unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
 9      /*以主设备号为索引 */
10      unsigned index = MAJOR(dev);
11      unsigned i;
12      struct probe *p;
13 
14      /*主设备超出255个 */
15      if (n > 255)
16          n = 255;
17      /*分配n个struct probe内存空间*/
18      p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
19      /*分配失败*/
20      if (p == NULL)
21          return -ENOMEM;
22      /*填装n个struct probe,对应n个主设备号
23            *
24            */
25      for (i = 0; i < n; i++, p++) {
26          p->owner = module;
27          p->get = probe;
28          p->lock = lock;
29          p->dev = dev;
30          p->range = range;
31          p->data = data;
32      }
33      /*进入临界区*/
34      mutex_lock(domain->lock);
35      /*这里p -= n是因为,在以上for循环中,p++了n次 */
36      for (i = 0, p -= n; i < n; i++, p++, index++) {
37          /*根据当前索引,从probes[]中
38           *取出一个probe
39                */
40          struct probe **s = &domain->probes[index % 255];
41          /*probe是一个链表,每个新加入的节点,
42            * 按照其range的大小,从小到大排列,即头结点的
43            * range是最小的
44            */
45          while (*s && (*s)->range < range)
46              /*继续查找下一个probe,直到其range大于
47                        * 或等于新加入probe的range为止
48                    */
49              s = &(*s)->next;
50          /* 找到了一个probe,其range大于或等于新加入
51                * probe的range,把这个新加入的probe下一节点指向
52                * 这个probe节点
53                */
54          p->next = *s;
55          /*新加入的节点代替旧的位置 */
56          *s = p;
57      }
58      /*退出临界区*/
59      mutex_unlock(domain->lock);
60      return 0;
61  }

 结构体的定义如下:

 1 struct kobj_map {
 2     struct probe {
 3         struct probe *next;
 4         dev_t dev;
 5         unsigned long range;
 6         struct module *owner;
 7         kobj_probe_t *get;
 8         int (*lock)(dev_t, void *);
 9         void *data;
10     } *probes[255];
11     struct mutex *lock;
12 };

参考博文:https://www.cnblogs.com/hackfun/p/5696289.html

posted @ 2020-07-05 17:33  Action_er  阅读(294)  评论(0编辑  收藏  举报