字符设备驱动程序实例

1:memdev.h

  1 #ifndef MEMDEV_H_
  2 #define MEMDEV_H_
  3 #ifndef MEMDEV_MAJOR
  4 #define MEMDEV_MAJOR 179 /*预设的mem的主设备号*/
  5 #endif
  6
  7 #ifndef MEMDEV_NR_DEVS
  8 #define MEMDEV_NR_DEVS 2 /*设备数*/
  9 #endif
 10
 11 #ifndef MEMDEV_SIZE
 12 #define MEMDEV_SIZE 4096
 13 #endif
 14
 15 /*mem设备描述结构体*/
 16 struct mem_dev
 17 {
 18   char *data;
 19   unsigned long size;
 20 };
 21 #endif
~          

2:memdev.c

  #include <linux/module.h>
  2   #include <linux/module.h>
  3   #include <linux/kernel.h>
  4   #include <linux/init.h>
  5   #include <linux/fs.h>
  6   #include <linux/cdev.h>
  7   #include <linux/device.h>
  8   #include <asm/uaccess.h>
  9   #include <linux/slab.h>
 10   #include"memdev.h"
 11     static  int mem_major = MEMDEV_MAJOR;
 12
 13     module_param(mem_major, int, S_IRUGO);
 14
 15     struct mem_dev *mem_devp; /*设备结构体指针*/
 16
 17     struct cdev cdev;
 18
 19     /*文件打开函数*/
 20     int mem_open(struct inode *inode, struct file *filp)
 21     {
 22         struct mem_dev *dev;
 23
 24         /*获取次设备号*/
 25         int num = MINOR(inode->i_rdev);
 26
 27         if (num >= MEMDEV_NR_DEVS)
 28                 return -ENODEV;
 29         dev = &mem_devp[num];
 30
 31         /*将设备描述结构指针赋值给文件私有数据指针*/
 32         filp->private_data = dev;
 33
 34         return 0;
 35     }
 36
 37     /*文件释放函数*/
 38     int mem_release(struct inode *inode, struct file *filp)
 39     {
 40       return 0;
 41     }
 42
 43     /*读函数*/
 44     static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, lof    f_t *ppos)
 45     {
 46       unsigned long p = *ppos; /*记录文件指针偏移位置*/
 47       unsigned int count = size; /*记录需要读取的字节数*/
 48       int ret = 0; /*返回值*/
 49       struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 50
 51       /*判断读位置是否有效*/
 52       if (p >= MEMDEV_SIZE) /*要读取的偏移大于设备的内存空间*/
 53         return 0;
 54       if (count > MEMDEV_SIZE - p) /*要读取的字节大于设备的内存空间*/
 55         count = MEMDEV_SIZE - p;
 57       /*读数据到用户空间:内核空间->用户空间交换数据*/
 58       if (copy_to_user(buf, (void*)(dev->data + p), count))
 59       {
 60         ret = - EFAULT;
 61       }
 62       else
 63       {
 64         *ppos += count;
 65         ret = count;
 66
 67         printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
 68       }
 69
 70       return ret;
 71     }
 72
 73     /*写函数*/
 74     static ssize_t mem_write(struct file *filp, const char __user *buf, size_t si    ze, loff_t *ppos)
 75     {
 76       unsigned long p = *ppos;
 77       unsigned int count = size;
 78       int ret = 0;
 79       struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 80
 81       /*分析和获取有效的写长度*/
 82       if (p >= MEMDEV_SIZE)
 83         return 0;
 84       if (count > MEMDEV_SIZE - p) /*要写入的字节大于设备的内存空间*/
 85         count = MEMDEV_SIZE - p;
 86
 87       /*从用户空间写入数据*/
 88       if (copy_from_user(dev->data + p, buf, count))
 89         ret = - EFAULT;
 90       else
 91       {
 92         *ppos += count; /*增加偏移位置*/
 93         ret = count; /*返回实际的写入字节数*/
 94
 95         printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
 96       }
 97
 98       return ret;
 99     }
100
101     /* seek文件定位函数 */
102     static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
103     {
        loff_t newpos;
105
106         switch(whence) {
107           case 0: /* SEEK_SET */ /*相对文件开始位置偏移*/
108             newpos = offset; /*更新文件指针位置*/
109             break;
110
111           case 1: /* SEEK_CUR */
112             newpos = filp->f_pos + offset;
113             break;
114
115           case 2: /* SEEK_END */
116             newpos = MEMDEV_SIZE -1 + offset;
117             break;
118
119           default: /* can't happen */
120             return -EINVAL;
121         }
122         if ((newpos<0) || (newpos>MEMDEV_SIZE))
123             return -EINVAL;
124
125         filp->f_pos = newpos;
126         return newpos;
127
128     }
129
130     /*文件操作结构体*/
131     static const struct file_operations mem_fops =
132     {
133       .owner = THIS_MODULE,
134       .llseek = mem_llseek,
135       .read = mem_read,
136       .write = mem_write,
137       .open = mem_open,
138       .release = mem_release,
139     };
140
141     /*设备驱动模块加载函数*/
142     static int memdev_init(void)
143     {
144       int result;
145       int i;
146   dev_t devno = MKDEV(mem_major, 0);
148
149        /* 申请设备号,当xxx_major不为0时,表示静态指定;当为0时,表示动态申请*/
150       /* 静态申请设备号*/
151       if (mem_major)
152         result = register_chrdev_region(devno, 2, "memdev");
153       else /* 动态分配设备号 */
154       {
155         result = alloc_chrdev_region(&devno, 0, 2, "memdev");
156         mem_major = MAJOR(devno); /*获得申请的主设备号*/
157       }
158
159       if (result < 0)
160         return result;
161
162      /*初始化cdev结构,并传递file_operations结构指针*/
163       cdev_init(&cdev, &mem_fops);
164       cdev.owner = THIS_MODULE; /*指定所属模块*/
165       cdev.ops = &mem_fops;
166
167       /* 注册字符设备 */
168       cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
169
170       /* 为设备描述结构分配内存*/
171       mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
172       if (!mem_devp) /*申请失败*/
173       {
174         result = - ENOMEM;
175         goto fail_malloc;
176       }
177       memset(mem_devp, 0, sizeof(struct mem_dev));
178
179       /*为设备分配内存*/
180       for (i=0; i < MEMDEV_NR_DEVS; i++)
181       {
182             mem_devp[i].size = MEMDEV_SIZE;
183             mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
184             memset(mem_devp[i].data, 0, MEMDEV_SIZE);
185       }
186
187       return 0;

  fail_malloc:
190       unregister_chrdev_region(devno, 1);
191
192       return result;
193     }
194
195     /*模块卸载函数*/
196     static void memdev_exit(void)
197     {
198       cdev_del(&cdev); /*注销设备*/
199       kfree(mem_devp); /*释放设备结构体内存*/
200       unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
201     }
202
203     MODULE_AUTHOR("David Xie");
204     MODULE_LICENSE("GPL");
205
206     module_init(memdev_init);
207     module_exit(memdev_exit);
makefile 编写:
1 ifeq ($(KERNELRELEASE),)
  2 #KERNEL_DIR:=/home/archermind/zhaoxi/bsw_ww02_2016/kernel/cht
  3 KERNEL_DIR:=/usr/src/linux-headers-3.13.0-32-generic
  4 PWD:=$(shell pwd)
  5 modules:
  6     $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
  7 modules_install:
  8     $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules_install
  9 clean:
 10     rm -rf  .*.cmd *.ko  *.o modules.order  Module.symvers *mod.c
 11     .PHONY: modules modules_install clean
 12 else
 13     modules-objs := memdev.o
 14     obj-m := memdev.o
 15 endif
~           
3:加载驱动:make

sudo insmod memdev.ko(如果出现insmod: error inserting 'memdev.ko': -1 Device or resource busy)查看 cat /proc/devices设备号和自己注册的是不是存在冲突导致加载失败)

修改头文件宏#define MEMDEV_MAJOR 主节点号

在/sys/modules会出先设备文件->需要在/dev下创建节点号,mknod /dev/memdev0 c 主设备号 次设备号 (例如上述代码不休改,则是sudo mknod /dev/memdev0 c 179 0)

用户空间的操作test.c

  1     #include <stdio.h>
  2
  3     int main()
  4     {
  5         FILE *fp0 = NULL;
  6         char Buf[4096];
  7
  8         /*初始化Buf*/
  9         strcpy(Buf,"Mem is char dev!");
 10         printf("BUF: %s\n",Buf);
 11
 12         /*打开设备文件*/
 13         fp0 = fopen("/dev/memdev0","r+");
 14         if (fp0 == NULL)
 15         {
 16             printf("Open Memdev0 Error!\n");
 17             return -1;
 18         }
 19
 20         /*写入设备*/
 21         fwrite(Buf, sizeof(Buf), 1, fp0);
 22
 23         /*重新定位文件位置(思考没有该指令,会有何后果)*/
 24         fseek(fp0,0,SEEK_SET);
 25
 26         /*清除Buf*/
 27         strcpy(Buf,"Buf is NULL!");
 28         printf("BUF: %s\n",Buf);
 29
 30
 31         /*读出设备*/
 32         fread(Buf, sizeof(Buf), 1, fp0);
 33
 34         /*检测结果*/
 35         printf("BUF: %s\n",Buf);
 36
 37         return 0;
 38
 39     }
~           

 

posted @ 2016-04-08 15:13  OracleLoyal  阅读(809)  评论(0编辑  收藏  举报