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 cdevowner欄位都必須要自行設定。

: 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

創作者介紹

十年磨一劍

flykof 發表在 痞客邦 PIXNET 留言(0) 人氣()