字符设备驱动的组成

1.在字符设备驱动模块加载函数中应该实现设备号的申请和cdev 的注册,而在卸载函数中应实现设备号的释放和cdev 的注销。

1//设备结构体
2 struct xxx_dev_t
3 {
4 struct cdev cdev;
5 ...
6 } xxx_dev;
7 //设备驱动模块加载函数
8 static int _ _init xxx_init(void)
9 {
10 ...
11 cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev
12 xxx_dev.cdev.owner = THIS_MODULE;
13 //获取字符设备号
14 if (xxx_major)
15 {
16 register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
17 }
18 else
19 {
20 alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
21 }
22
23 ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //注册设备
24 ...
25 }
26 /*设备驱动模块卸载函数*/
27 static void _ _exit xxx_exit(void)
28 {
29 unregister_chrdev_region(xxx_dev_no, 1); //释放占用的设备号
30 cdev_del(&xxx_dev.cdev); //注销设备
31 ...
32 }

2.file_operations 结构体中成员函数是字符设备驱动与内核的接口,是用户空间对Linux 进行系统调用最终的落实者。大多数字符设备驱动会实现read()、write()和ioctl()函数。

1 struct file_operations xxx_fops =
2 {
3 .owner = THIS_MODULE,
4 .read = xxx_read,
5 .write = xxx_write,
6 .ioctl = xxx_ioctl,
7 ...
8 };

下面将基于虚拟的 globalmem 设备进行字符设备驱动。globalmem 意味着“全局内存”,在globalmem 字符设备驱动中会分配一片大小为GLOBALMEM_ SIZE的内存空
间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过Linux 系统调用访问这片内存。

  1 #include<linux/module.h>
  2 #include<linux/types.h>
  3 #include<linux/fs.h>
  4 #include<linux/errno.h>
  5 #include<linux/mm.h>
  6 #include<linux/sched.h>
  7 #include<linux/init.h>
  8 #include<linux/cdev.h>
  9 #include<linux/slab.h>
 10 #include<linux/kdev_t.h>
 11 #include<linux/device.h>
 12 #include <asm/io.h>
 13 #include<asm/system.h>
 14 #include<asm/uaccess.h>
 15 
 16 #define GLOBALMEM_SIZE 0x1000
 17 #define MEM_CLEAR 0x1
 18 #define GLOBALMEM_MAJOR 254  /*预设的globalmem 的主设备号*/
 19 
 20 static int globalmem_major = GLOBALMEM_MAJOR;
 21 /*globalmem 设备结构体*/
 22 struct globalmem_dev
 23 {
 24     struct cdev cdev;
 25     unsigned char mem[GLOBALMEM_SIZE];
 26 };
 27 struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
 28 /*文件打开函数*/
 29 int globalmem_open(struct inode *inode, struct file *filp)
 30 {
 31     filp->private_data = globalmem_devp;/*将设备结构体指针赋值给文件私有数据指针*/
 32     return 0;
 33 }
 34 /*文件释放函数*/
 35 int globalmem_release(struct inode *inode, struct file *filp)
 36 {
 37     return 0;
 38 }
 39 /* ioctl 设备控制函数 */
 40 static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
 41 {
 42     struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/
 43     switch (cmd)
 44     {
 45         case MEM_CLEAR:
 46             memset(dev->mem, 0, GLOBALMEM_SIZE);
 47             printk(KERN_INFO "globalmem is set to zero
");
 48         break;
 49         default:return - EINVAL;
 50     }
 51     return 0;
 52 }
 53 /*读函数*/
 54 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
 55 {
 56     unsigned long p = *ppos;
 57     unsigned int count = size;
 58     int ret = 0;
 59     struct globalmem_dev *dev = filp->private_data;
 60     if (p >= GLOBALMEM_SIZE)
 61         return count ? - ENXIO: 0;
 62     if (count > GLOBALMEM_SIZE - p)
 63         count = GLOBALMEM_SIZE - p;
 64     /*内核空间→用户空间*/
 65     if (copy_to_user(buf, (void*)(dev->mem + p), count))
 66     {
 67         ret = - EFAULT;
 68     }
 69     else
 70     {
 71         *ppos += count;
 72         ret = count;
 73         printk(KERN_INFO "read %d bytes(s) from %d
",count,p);
 74     }
 75     return ret;
 76 }
 77 /*写函数*/
 78 static ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
 79 {
 80     unsigned long p = *ppos;
 81     unsigned int count = size;
 82     int ret = 0;
 83     struct globalmem_dev *dev = filp->private_data;
 84     if (p >= GLOBALMEM_SIZE)
 85         return count ? - ENXIO: 0;
 86     if (count > GLOBALMEM_SIZE - p)
 87         count = GLOBALMEM_SIZE - p;
 88     /*用户空间→内核空间*/
 89    if (copy_from_user(dev->mem + p, buf, count))
 90     {
 91         ret = - EFAULT;
 92     }
 93     else
 94     {
 95         *ppos += count;
 96         ret = count;
 97         printk(KERN_INFO "written %d bytes(s) from %d
", count, p);
 98     }
 99     return ret;
100 }
101 /* seek 文件定位函数 */
102  static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
103 {
104     loff_t ret = 0;
105     switch(orig)
106     {
107         case 0:   /*相对文件开始位置偏移*/
108             if(offset < 0)
109             {
110                 ret = -EINVAL;//
111                 break;
112             }
113             if((unsigned int)offset > GLOBALMEM_SIZE)
114             {
115                 ret = -EINVAL;
116                 break;
117             }
118             filp->f_pos = (unsigned int)offset;
119             ret = filp->f_pos;
120             break;
121         case 1:    /*相对文件当前位置偏移*/
122             if((filp->f_pos + offset) > GLOBALMEM_SIZE)
123             {
124                 ret = -EINVAL;
125                 break;
126             }
127             if((filp->f_pos + offset) < 0)
128             {
129                 ret = -EINVAL;
130                 break;
131             }
132             filp->f_pos +=offset;
133             ret = filp->f_pos;
134             break;
135         default:
136             ret = -EINVAL;
137             break;
138     }
139     return ret;
140 }
141 /*文件操作结构体*/
142 static const struct file_operations globalmem_fops=
143 {
144     .owner = THIS_MODULE,
145     .llseek = globalmem_llseek,
146     .read = globalmem_read,
147     .write = globalmem_write,
148     .ioctl = globalmem_ioctl,
149     .open = globalmem_open,
150     .release = globalmem_release,
151 };
152 /*初始化并注册cdev*/
153 static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
154 {
155     int err,devno = MKDEV(globalmem_major,index);
156     cdev_init(&dev->cdev,&globalmem_fops);
157     dev->cdev.owner = THIS_MODULE;
158     err = cdev_add(&dev->cdev,devno,1);
159     if(err)
160         printk(KERN_INFO"error %d ading globalmem %d",err,index);
161 }
162 /*设备驱动模块加载函数*/
163 int globalmem_init(void)
164 {
165     int result;
166     dev_t devno = MKDEV(globalmem_major,0);
167     if(globalmem_major)  /* 申请设备号*/
168     {
169         result = register_chrdev_region(devno,1,"globalmem");
170         printk("register_chrdev_region
");
171     }
172     else   /* 动态申请设备号 */
173     {
174         result = alloc_chrdev_region(&devno,0,1,"globalmem");
175         globalmem_major = MAJOR(devno);
176         printk("alloc_chrdev_region
");
177     }
178     if(result < 0)
179         return result;
180      /* 动态申请设备结构体的内存*/
181     globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
182     if(!globalmem_devp)
183     {
184         result = -ENOMEM;//no memory
185         goto fail_malloc;
186     }
187     memset(globalmem_devp,0,sizeof(struct globalmem_dev));
188     globalmem_setup_cdev(globalmem_devp,0);
189     printk("init ok..
");
190     return 0;
191 fail_malloc:
192     unregister_chrdev_region(devno,1);
193     return result;
194 }
195 /*模块卸载函数*/
196 void globalmem_exit(void)
197 {
198     cdev_del(&globalmem_devp->cdev);   /*注销cdev*/
199     kfree(globalmem_devp);
200     unregister_chrdev_region(MKDEV(globalmem_major,0),1); /*释放设备号*/
201     printk("exit..
");
202 }
203 MODULE_AUTHOR("Huang");
204 MODULE_LICENSE("Dual BSD/GPL");
205 
206 module_param(globalmem_major, int, S_IRUGO);/*内核模块通过module_param()来传递参数*/
207 module_init(globalmem_init);
208 module_exit(globalmem_exit);

Makefile:

 1 ifneq ($(KERNELRELEASE),)
 2     obj-m:= globalmem.o
 3 else
 4     PWD:=$(shell pwd)
 5     KVER?=$(shell uname -r)
 6     KERNELDIR:= /usr/src/linux-2.6.32.2     //编译好的内核
 7 default:
 8     make -C $(KERNELDIR) M=$(PWD) modules
 9 endif
10 clean:
11     rm-f *.ko *.mod.c *.mod.o *.o

运行 insmod globalmem.ko 命令加载模块,通过 lsmod 命令,发现globalmem 模块已被加载。再通过 cat /proc/devices 命令查看,发现多出了主设备号为254的globalmem字符设备驱动。

原文地址:https://www.cnblogs.com/ht-beyond/p/4313927.html