您的位置:首页 > 其它

KDD字符设备基本概念

2013-10-02 16:46 169 查看
数据结构及关系如下:

include/linux/cdev.h
12 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.h
518 /*
519  * Keep mostly read-only and often accessed (especially for
520  * the RCU path lookup and 'stat' data) fields at the beginning
521  * 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.h
765 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.h
struct 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";

/**
make

insmod hello.ko \ && major_device_number=`awk '$2=="winlin" {print $1}' /proc/devices` \ && mknod /dev/winlin c $major_device_number 0
cat /dev/winlin \ && echo "linux is a great operating system for server." > /dev/winlin \ && cat /dev/winlin
rm -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.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
rm -f *.mod.* *.ko *.order *.symvers *.o

endif


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

// hello.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");

int major = 0;
int count = 1;
char* name = "winlin";

/**
make

insmod 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 1

cat /dev/winlin0 \
&& echo "[dev0] linux is a great operating system for server." > /dev/winlin0 \
&& cat /dev/winlin0

cat /dev/winlin1 \
&& echo "[dev1] linux is a great operating system for server." > /dev/winlin1 \
&& cat /dev/winlin1

rm -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";

/**
make

insmod 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 1

cat /dev/winlin0 \
&& echo "[dev0] linux is a great operating system for server." > /dev/winlin0 \
&& cat /dev/winlin0

cat /dev/winlin1 \
&& echo "[dev1] linux is a great operating system for server." > /dev/winlin1 \
&& cat /dev/winlin1

rm -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.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
rm -f *.mod.* *.ko *.order *.symvers *.o

install:
bash install.sh

uninstall:
rm -f /dev/winlin*
rmmod hello

endif


安装脚本:

#!/bin/bash
#install.sh
insmod hello.ko count=2
major_device_number=`cat /proc/devices|grep winlin|awk '{print $1}'`
mknod /dev/winlin0 c ${major_device_number} 0
mknod /dev/winlin1 c ${major_device_number} 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: