Linux Device Drivers ─
Register Character Device
要讓核心能提調你的驅動程式提供的作業方法,你必須配置並註冊一或多個struct cdev。用於配置struct cdev的函式是cdev_alloc(),你可再執行其配置一個獨立的cdev結構,然後將其ops欄位指向你的file_operations結構:
Struct cdev *my_cdev = cdev_alloc();
My_cdev->ops = &my_fops;
如果你的驅動程式需要將cdev嵌在你自己設計的一個特殊資料結構中,必須改用cdev_init()函式來初始化所得到的cdev:
Void cdev_init(struct cdev *dev, struct file_operations *fops);
無論如何初始化struct cdev,owner欄位都必須要自行設定。
如: struct cdev my_cdev;
my_cdev.owner = THIS_MODULE;
最後一步是以cdev_add()將它加入核心:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
dev:是你設定好的cdev結構
num:是第一個裝置編號
count:是裝置編號的總數(通常設為1,除非要同時應付多個裝置編號。)
使用cdev_add()時要注意它有失敗的機率雖然很低,因此,除非驅動程式已經完全準備妥當,而且目標裝置也準備好運作,否則不應該呼叫。
註銷cdev,只要呼叫一次cdev_del()即可:
void cdev_del(struct cdev *dev);
範例:
dev_reg.c
#include <linux/init.h> /* modules */
#include <linux/module.h> /* module */
#include <linux/types.h> /* dev_t type */
#include <linux/kernel.h>
#include <linux/fs.h> /* chrdev allocation */
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/current.h> /* Proccess */
#include <asm/uaccess.h>
#include "dev_reg.h"
#define DRIVER_NAME "dev_reg"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Emil Chen");
MODULE_DESCRIPTION("Register Character Device");
dev_t dev;
struct cdev dev_reg_cdev;
static unsigned int dev_reg_major = 0;
module_param(dev_reg_major, uint, 0);
static int dev_reg_open(struct inode *inode, struct file *file)
{ /* open handler */
printk("%s: major %d minor %d (pid %d)\n", __func__,
imajor(inode),
iminor(inode),
current->pid
);
inode->i_private = inode;
file->private_data = file;
printk(" i_private=%p private_data=%p\n",
inode->i_private, file->private_data);
return 0; /* success */
}
static int dev_reg_close(struct inode *inode, struct file *file)
{ /* close handler */
printk("%s: major %d minor %d (pid %d)\n", __func__,
imajor(inode),
iminor(inode),
current->pid );
printk(" i_private=%p private_data=%p\n",
inode->i_private, file->private_data);
return 0; /* success */
}
struct file_operations dev_reg_fops = {
.open = dev_reg_open,
.release = dev_reg_close,
};
static int __init_dev(void)
{
int alloc_ret = 0, cdev_err = 0;
// alloc_chrdev_region return 0 on success
alloc_ret = alloc_chrdev_region(&dev, DEVNUM_MINOR_START, DEVNUM_COUNT, DEVNUM_NAME);
if(alloc_ret){
printk(KERN_WARNING "%s : could not allocate device\n", __func__);
goto error;
}
else{
printk(KERN_WARNING "%s : registered with major number:%i, minor number:%i\n",
__func__, MAJOR(dev), MINOR(dev));
dev_reg_major = MAJOR(dev);
}
cdev_init(&dev_reg_cdev, &dev_reg_fops);
dev_reg_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&dev_reg_cdev, MKDEV(dev_reg_major, 0), DEVNUM_COUNT);
if(cdev_err){
printk(KERN_WARNING "%s : could not add cdev\n", __func__);
goto error;
}
printk(KERN_ALERT "%s driver(major %d) installed.\n", DRIVER_NAME, dev_reg_major);
return 0;
error:
if (cdev_err == 0)
cdev_del(&dev_reg_cdev);
if (alloc_ret == 0)
unregister_chrdev_region(dev, DEVNUM_COUNT);
return -1;
}
static void __exit_dev(void)
{
printk(KERN_INFO "dev_num Module removed.\n");
/* Free the devices */
cdev_del(&dev_reg_cdev);
unregister_chrdev_region(dev,DEVNUM_COUNT);
}
module_init(__init_dev);
module_exit(__exit_dev);
dev_reg.h
#ifndef __DEVNUM1_H_
#define __DEVNUM1_H_
// Number of devices
#define DEVNUM_COUNT 4
// Name of the driver
#define DEVNUM_NAME "dev_reg"
// First minor number
#define DEVNUM_MINOR_START 0
#endif /* dev_num.h included */
Makefile
APP=ldd
KERNELDIR ?= $(shell pwd)/../../..
PWD := $(shell pwd)
obj-m := $(APP).o
$(APP)-objs := dev_reg.o
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define DEVFILE "/dev/dev_reg"
int open_file(void)
{
int fd;
fd = open(DEVFILE, O_RDWR);
if (fd == -1) {
perror("open");
}
return (fd);
}
void close_file(int fd)
{
if (close(fd) != 0) {
perror("close");
}
}
int main(void)
{
int fd;
fd = open_file();
sleep(20);
close_file(fd);
return 0;
}
Makefile
#
#
# Makefile for the linux version of simple
#
CC=/opt/buildroot-gcc342/bin/mipsel-linux-cc
EXEC = test
OBJS = test.o
#CFLAGS += -DDEBUG
CFLAGS +=
all: $(EXEC)
$(EXEC): $(OBJS) $(CONF_H)
$(CC) $(CFLAGS) -o $@ $(OBJS)
romfs:
$(ROMFSINST) /bin/$(EXEC)
clean:
-rm -f $(EXEC) *.o
測試結果
# insmod ldd.ko
__init_dev : registered with major number:251, minor number:0
dev_reg driver(major 251) installed.
#
# cat /proc/devices | grep dev_reg
251 dev_reg
#
# mknod /dev/dev_reg c 251 0
#
# ls /dev/dev*
/dev/dev_reg
#
# cat /dev/dev_reg
dev_reg_open: major 251 minor 0 (pid 2131)
i_private=804d5bb0 private_data=815d2340
cat: read error:dev_reg_close: major 251 minor 0 (pid 2131)
Invalid argumen i_private=804d5bb0 private_data=815d2340
t
#
# ./test &
dev_reg_open: major 251 minor 0 (pid 2132)
i_private=804d5bb0 private_data=815d2340
dev_reg_close: major 251 minor 0 (pid 2132)
i_private=804d5bb0 private_data=815d2340
舊式的註冊方法為:
int register_chrdev(insigned int major, const char *name,
struct file_operations *fops);
註銷為:
int unregister_chrdev(unsigned int major, const char *name);
範例:
devone.c
/* legacy\devone.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
#define DRIVER_NAME "devone"
static unsigned int devone_major = 0;
module_param(devone_major, uint, 0);
static int devone_open(struct inode *inode, struct file *file)
{ /* open handler */
printk("%s: major %d minor %d (pid %d)\n", __func__,
imajor(inode),
iminor(inode),
current->pid
);
inode->i_private = inode;
file->private_data = file;
printk(" i_private=%p private_data=%p\n",
inode->i_private, file->private_data);
return 0; /* success */
}
static int devone_close(struct inode *inode, struct file *file)
{ /* close handler */
printk("%s: major %d minor %d (pid %d)\n", __func__,
imajor(inode),
iminor(inode),
current->pid );
printk(" i_private=%p private_data=%p\n",
inode->i_private, file->private_data);
return 0; /* success */
}
struct file_operations devone_fops = {
.open = devone_open,
.release = devone_close,
};
static int devone_init(void)
{
int major;
int ret = 0;
major = register_chrdev(devone_major, DRIVER_NAME,
&devone_fops);
if ((devone_major > 0 && major != 0) || /*static allocation */
(devone_major == 0 && major < 0) || /*dynamic allocation */
major < 0) { /* else */
printk("%s driver registration error\n",
DRIVER_NAME);
ret = major;
goto error;
}
if (devone_major == 0) { /* dynamic allocation */
devone_major = major;
}
printk("%s driver(major %d) installed.\n",
DRIVER_NAME, devone_major);
error:
return (ret);
}
static void devone_exit(void)
{
unregister_chrdev(devone_major, DRIVER_NAME);
printk("%s driver removed.\n", DRIVER_NAME);
}
module_init(devone_init);
module_exit(devone_exit);
Makefile
CFLAGS += -Wall
CFILES = devone.c
APP=sample
KERNELDIR ?= $(shell pwd)/../../..
PWD := $(shell pwd)
obj-m := $(APP).o
$(APP)-objs := $(CFILES:.c=.o)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
留言列表