KDD字符设备基本概念

来源:互联网 发布:喝咖啡会难怀孕吗 知乎 编辑:程序博客网 时间:2024/05/17 23:39

数据结构及关系如下:

include/linux/cdev.h12 struct cdev {13         struct kobject kobj;14         struct module *owner;15         const struct file_operations *ops;16         struct list_head list;17         dev_t dev;18         unsigned int count;19 };include/linux/fs.h518 /*519  * Keep mostly read-only and often accessed (especially for520  * the RCU path lookup and 'stat' data) fields at the beginning521  * of the 'struct inode'522  */523 struct inode {595         union {596                 struct pipe_inode_info  *i_pipe;597                 struct block_device     *i_bdev;598                 struct cdev             *i_cdev;599         };612 };include/linux/fs.h765 struct file {777         struct inode            *f_inode;       /* cached value */800         /* needed for tty driver, and maybe others */801         void                    *private_data;812 };

struct cdev表示字符设备,ops为文件操作函数,dev为设备号:

// include/linux/cdev.hstruct cdev {    struct module *owner;    const struct file_operations *ops;    dev_t dev;    // ......};

关联cdev和ops的函数是cdev_init:

void cdev_init(struct cdev *, const struct file_operations *);

关联cdev和dev的函数是cdev_add,即添加字符设备:

int cdev_add(struct cdev *, dev_t, unsigned);

添加字符设备后,就可以用mknod创建文件,可以读写。

创建设备号时,需要指定设备名,即函数alloc_chrdev_region。

mknod需要知道设备名和设备号,安装驱动后,可以在/proc/devices中依据设备名查到设备号:

insmod hello.ko \&& major_device_number=`awk '$2=="winlin" {print $1}' /proc/devices` \&& mknod /dev/winlin c $major_device_number 0

创建文件后,就可以读写这个文件/dev/winlin了:

cat /dev/winlin \&& echo "linux is a great operating system for server." > /dev/winlin \&& cat /dev/winlin

最后,删除设备和驱动:

rm -f /dev/winlin \&& rmmod hello

完整源码如下:

// hello.c#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("GPL");dev_t devno;int count = 1;char* name = "winlin";/**makeinsmod hello.ko \&& major_device_number=`awk '$2=="winlin" {print $1}' /proc/devices` \&& mknod /dev/winlin c $major_device_number 0cat /dev/winlin \&& echo "linux is a great operating system for server." > /dev/winlin \&& cat /dev/winlinrm -f /dev/winlin \&& rmmod hello*//*module_param(count, int, S_IRUGO|S_IWUSR);*/module_param(name, charp, S_IRUGO|S_IWUSR);#include <linux/fs.h>#include <linux/uaccess.h>char _data[1024] = "KDD char device buffer.\n";ssize_t hello_read(struct file* file,     char __user* data, size_t size, loff_t* offset){    int len = strlen(_data);        if(*offset >= len){        return 0;    }        printk(KERN_ALERT        "hello_read, len=%d, size=%d, offset=%d\n",         len, (int)size, (int)*offset);            if(copy_to_user(data, _data, (len < size)? len:size) != 0){        return -1;    }        *offset += len;    return len;}ssize_t hello_write(struct file* file,     const char __user* data, size_t size, loff_t* offset){    int size_to_write = (sizeof(_data) - 1 < size)? sizeof(_data) - 1:size;        printk(KERN_ALERT        "hello_write, len=%d, size=%d, offset=%d\n",         size_to_write, (int)size, (int)*offset);            if(copy_from_user(_data, data, size_to_write) != 0){        return -1;    }        _data[size_to_write] = 0;        return size_to_write;}int hello_open(struct inode* node, struct file* file){    return 0;}int hello_release(struct inode* node, struct file* file){    return 0;}struct file_operations fops = {    .owner = THIS_MODULE,    .read = hello_read,    .write = hello_write,    .open = hello_open,    .release = hello_release,};#include <linux/cdev.h>struct cdev dev;int hello_init(void){    printk(KERN_INFO "hello, %s!\n", name);        if(alloc_chrdev_region(&devno, 0, count, name) != 0){        return -1;    }        cdev_init(&dev, &fops);    dev.owner = THIS_MODULE;    //dev.ops = &fops;        if(cdev_add(&dev, devno, 1) != 0){        return -1;    }        return 0;}void hello_exit(void){    printk(KERN_ALERT "goodbye, %s\n", name);        unregister_chrdev_region(devno, count);}module_init(hello_init);module_exit(hello_exit);

Makefile如下:

ifneq ($(KERNELRELEASE),)    obj-m := hello.oelse    KERNELDIR ?= /lib/modules/$(shell uname -r)/build    PWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:rm -f *.mod.* *.ko *.order *.symvers *.oendif

一个驱动可以创建多个设备,即minor不一样,major一样。代码如下:

// hello.c#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("GPL");int major = 0;int count = 1;char* name = "winlin";/**makeinsmod hello.ko count=2\&& major_device_number=`awk '$2=="winlin" {print $1}' /proc/devices` \&& mknod /dev/winlin0 c $major_device_number 0 \&& mknod /dev/winlin1 c $major_device_number 1cat /dev/winlin0 \&& echo "[dev0] linux is a great operating system for server." > /dev/winlin0 \&& cat /dev/winlin0cat /dev/winlin1 \&& echo "[dev1] linux is a great operating system for server." > /dev/winlin1 \&& cat /dev/winlin1rm -f /dev/winlin* \&& rmmod hello*/module_param(count, int, S_IRUGO|S_IWUSR);module_param(name, charp, S_IRUGO|S_IWUSR);#include <linux/fs.h>#include <linux/uaccess.h>char _data[1024] = "KDD char device buffer.\n";ssize_t hello_read(struct file* file,     char __user* data, size_t size, loff_t* offset){    int len = strlen(_data);        if(*offset >= len){        return 0;    }        printk(KERN_ALERT        "hello_read, len=%d, size=%d, offset=%d\n",         len, (int)size, (int)*offset);            if(copy_to_user(data, _data, (len < size)? len:size) != 0){        return -1;    }        *offset += len;    return len;}ssize_t hello_write(struct file* file,     const char __user* data, size_t size, loff_t* offset){    int size_to_write = (sizeof(_data) - 1 < size)? sizeof(_data) - 1:size;        printk(KERN_ALERT        "hello_write, len=%d, size=%d, offset=%d\n",         size_to_write, (int)size, (int)*offset);            if(copy_from_user(_data, data, size_to_write) != 0){        return -1;    }        _data[size_to_write] = 0;        return size_to_write;}int hello_open(struct inode* node, struct file* file){    printk(KERN_ALERT "hello_open file\n");    return 0;}int hello_release(struct inode* node, struct file* file){    printk(KERN_ALERT "hello_release file\n");    return 0;}struct file_operations fops = {    .owner = THIS_MODULE,    .read = hello_read,    .write = hello_write,    .open = hello_open,    .release = hello_release,};#include <linux/cdev.h>struct cdev dev;int hello_init(void){    int i;    dev_t devno;    printk(KERN_INFO "hello, %s!\n", name);        if(alloc_chrdev_region(&devno, 0, count, name) != 0){        return -1;    }    major = MAJOR(devno);        cdev_init(&dev, &fops);    dev.owner = THIS_MODULE;    //dev.ops = &fops;        for(i = 0; i < count; i++){        if(cdev_add(&dev, MKDEV(major, i), 1) != 0){            return -1;        }    }        return 0;}void hello_exit(void){    printk(KERN_ALERT "goodbye, %s\n", name);        cdev_del(&dev);    unregister_chrdev_region(MKDEV(major, 0), count);}module_init(hello_init);module_exit(hello_exit);

这两个设备都是共享一个cdev和data,可以让每个设备有不同的表示,即根据count来开辟一个结构体,这个结构体包含了cdev和data。

代码如下:

// hello.c#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("GPL");int major = 0;int count = 1;char* name = "winlin";/**makeinsmod hello.ko count=2\&& major_device_number=`awk '$2=="winlin" {print $1}' /proc/devices` \&& mknod /dev/winlin0 c $major_device_number 0 \&& mknod /dev/winlin1 c $major_device_number 1cat /dev/winlin0 \&& echo "[dev0] linux is a great operating system for server." > /dev/winlin0 \&& cat /dev/winlin0cat /dev/winlin1 \&& echo "[dev1] linux is a great operating system for server." > /dev/winlin1 \&& cat /dev/winlin1rm -f /dev/winlin* \&& rmmod hello*/module_param(count, int, S_IRUGO|S_IWUSR);module_param(name, charp, S_IRUGO|S_IWUSR);#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/cdev.h>struct winlin_dev{    dev_t devno;    char data[1024];    struct cdev cdev;};const char* msg = "KDD char device buffer.\n";struct winlin_dev* devs;ssize_t hello_read(struct file* file,     char __user* data, size_t size, loff_t* offset){    struct winlin_dev* dev = file->private_data;    int len = strlen(dev->data);        if(*offset >= len){        return 0;    }        printk(KERN_ALERT        "hello_read, len=%d, size=%d, offset=%d\n",         len, (int)size, (int)*offset);            if(copy_to_user(data, dev->data, (len < size)? len:size) != 0){        return -1;    }        *offset += len;    return len;}ssize_t hello_write(struct file* file,     const char __user* data, size_t size, loff_t* offset){    struct winlin_dev* dev = file->private_data;    int size_to_write = (sizeof(dev->data) - 1 < size)? sizeof(dev->data) - 1:size;        printk(KERN_ALERT        "hello_write, len=%d, size=%d, offset=%d\n",         size_to_write, (int)size, (int)*offset);            if(copy_from_user(dev->data, data, size_to_write) != 0){        return -1;    }        dev->data[size_to_write] = 0;        return size_to_write;}int hello_open(struct inode* node, struct file* file){    struct winlin_dev* dev = container_of(node->i_cdev, struct winlin_dev, cdev);    file->private_data = dev;    return 0;}int hello_release(struct inode* node, struct file* file){    return 0;}struct file_operations fops = {    .owner = THIS_MODULE,    .read = hello_read,    .write = hello_write,    .open = hello_open,    .release = hello_release,};int hello_init(void){    int i;    dev_t devno;    printk(KERN_INFO "hello, %s!\n", name);        if(alloc_chrdev_region(&devno, 0, count, name) != 0){        return -1;    }    major = MAJOR(devno);        devs = kmalloc(count * sizeof(struct winlin_dev), GFP_KERNEL);    if(!devs){        return -1;    }    memset(devs, 0, count * sizeof(struct winlin_dev));        for(i = 0; i < count; i++){        cdev_init(&devs[i].cdev, &fops);        devs[i].cdev.owner = THIS_MODULE;        //devs[i].cdev.ops = &fops;                devs[i].devno = MKDEV(major, i);        if(cdev_add(&devs[i].cdev, devs[i].devno, 1) != 0){            return -1;        }        memcpy(devs[i].data, msg, strlen(msg));    }        return 0;}void hello_exit(void){    int i;    printk(KERN_ALERT "goodbye, %s\n", name);        for(i = 0; i < count; i++){        cdev_del(&devs[i].cdev);    }    kfree(devs);        unregister_chrdev_region(MKDEV(major, 0), count);}module_init(hello_init);module_exit(hello_exit);

Makefile修改为:

ifneq ($(KERNELRELEASE),)    obj-m := hello.oelse    KERNELDIR ?= /lib/modules/$(shell uname -r)/build    PWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:rm -f *.mod.* *.ko *.order *.symvers *.oinstall:bash install.shuninstall:rm -f /dev/winlin*rmmod helloendif

安装脚本:

#!/bin/bash#install.shinsmod hello.ko count=2major_device_number=`cat /proc/devices|grep winlin|awk '{print $1}'`mknod /dev/winlin0 c ${major_device_number} 0mknod /dev/winlin1 c ${major_device_number} 1