Linux设备之字符驱动设备(一)

来源:互联网 发布:java bytebuffer长度 编辑:程序博客网 时间:2024/06/01 17:19

一、字符驱动设备
字符设备是能够像字节流(类似文件)一样被访问的设备,有字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read、write系统调用。

内核中使用cdev结构体来描述字符设备

struct cdev {          struct kobject kobj;          struct module *owner;          const struct file_operations *ops;          struct list_head list;          dev_t dev;          unsigned int count;  };

字符驱动的设备加载可以分为以下几个步骤:

1、分配cdev设备号
如果我们提前知道设备的编号,那么就用register_chrdev_region()
如果我们不知道,那么就用alloc_chrdev_region(),动态分配

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) {         struct char_device_struct *cd;         cd = __register_chrdev_region(0, baseminor, count, name);         if (IS_ERR(cd))                return PTR_ERR(cd);         *dev = MKDEV(cd->major, cd->baseminor);        return 0; }

2、初始化cdev
cdev_init,与file operation关联

void cdev_init(struct cdev *cdev, const struct file_operations *fops) {            memset(cdev, 0, sizeof *cdev);            INIT_LIST_HEAD(&cdev->list);            kobject_init(&cdev->kobj, &ktype_cdev_default);            cdev->ops = fops;}

3、注册cdev
cdev_add,添加字符设备

int cdev_add(struct cdev *p, dev_t dev, unsigned count) {         int error;         p->dev = dev;         p->count = count;         error = kobj_map(cdev_map, dev, count, NULL,         exact_match, exact_lock, p);         if (error)                return error;        kobject_get(p->kobj.parent);        return 0; }

linux内核源码网站:
http://lxr.free-electrons.com/ident

二、示例

代码一:简单字符驱动模块

#include "linux/init.h"#include "linux/module.h"#include <linux/sched.h>  //struct task_struct *current  #include <linux/fs.h>     //file_operation, file, inode  #include <linux/types.h>  //设备号相关,eg MAJOR, MINOR, MKDEV  #include <linux/cdev.h>   //字符设备相关操作,init,add,del  #include <linux/slab.h>   //kmalloc, kfree  #include <asm/uaccess.h>  //copy_to_user, copy_from_user  dev_t dev; //device numberint chardev_minor = 5; //字符设备的次设备号int chardev_count = 1; //字符设备的数量struct cdev *char_dev = NULL; //字符驱动设备int chardev_read( struct file *filp, char *buf, size_t count, loff_t *ppos )  {      if (filp->private_data==NULL)          printk(KERN_EMERG "in read: private data in file struct is NULL\n");      else {          printk(KERN_EMERG "in read: private data in file struct is not NULL\n");         /*将内核空间中filp->private_data指向的数据拷贝到用户空间buf */        copy_to_user(buf, filp->private_data, count);    }      kfree(filp->private_data);     filp->private_data = NULL;      return 0;  }  int chardev_write( struct file *filp, const char *buf, size_t count, loff_t *offp )  {      if (filp->private_data==NULL) {          printk("in write: private data in file struct is NULL\n");          filp->private_data = (void*)kmalloc(100, GFP_KERNEL);      }      else          printk("in write: private data in file struct is not NULL\n");      /*将用户空间buf中的数据拷贝到内核空间filp->private_data */    copy_from_user(filp->private_data, buf, count);    return 0;  }  struct file_operations file_ops = {    .owner =    THIS_MODULE,    .write = chardev_write,    .read = chardev_read,};static int __init chardev_init(void){    int ret = 0;    /*动态分配设备号*/    ret = alloc_chrdev_region(&dev, chardev_minor, chardev_count, "chardev");    if (ret) {        printk("alloc chrdev region fail!\n");        return -EINVAL;    } else {        printk("char device major device number %d\n", MAJOR(dev));        printk("char device minor device number %d\n", MINOR(dev));    }    /*为申请的字符设备分配内存*/    char_dev = cdev_alloc();    /*关联file_operation*/    char_dev->ops = &file_ops;    /*建立cdev和file operation之间的连接*/    cdev_init(char_dev, &file_ops);    printk("cdev init success!\n");    /*在系统中注册cdev*/    ret = cdev_add(char_dev, dev, 1);    if (ret) {        printk("cdev add failed!\n");        goto fail;    }    return 0;fail:    unregister_chrdev_region(dev, chardev_count);    return -EINVAL;}static int __exit chardev_exit(void){    /*释放设备号*/    unregister_chrdev_region(dev, chardev_count);    /*在系统中删除字符设备*/    cdev_del(char_dev);    printk("delete cdev success!\n");    return 0;}module_init(chardev_init);module_exit(chardev_exit);MODULE_LICENSE("GPL");

代码二:Makefile文件

obj-m := char_dev.o  KERNEL_DIR := /lib/modules/$(shell uname -r)/build  PWD := $(shell pwd)  all:      make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules  clean:      rm *.o *.ko *.mod.c  .PHONY:clean  

代码三:用户空间程序

int main(){    int fp, ret;      unsigned char ch[]="hello world,my char device!";      unsigned char *buf1 = (unsigned char *)malloc(sizeof(ch)+1);      unsigned char *buf2 = (unsigned char *)malloc(sizeof(ch)+1);      //init buff      memset(buf1, 0, sizeof(ch)+1);      memset(buf2, 0, sizeof(ch)+1);      strcpy(buf1, ch);//ch to buf1      //打开设备驱动,前提是用mknode把字符设备建立为/dev/char     fp = open("/dev/char_new_dev", O_RDONLY);      printf("fp is %d\n", fp);      ret = write(fp, buf1, sizeof(ch));//write buf1 to fp      printf("write return : %d\n", ret);      ret = read(fp, buf2, sizeof(ch));//read fp to buf2      printf("read return : %d\n", ret);      printf("read data:%s\n", buf2);     close(fp);      return 0;  }
0 0