2012年1月2日 星期一

基礎 Linux Device Driver 驅動程式#8 (character device driver基礎_chrdev_sys)

前面介紹了那麼多的範例,但您有發現嗎?
所有範例程式的裝置檔,都必須要手動建立,難道不能自動建立嗎?
有,就是 udev,那您會說, udev 是什麼?
簡單,上wiki去查一下就有啦。
它是 Linux kernel 2.6系列的裝置管理器。
更詳細的說明,去wiki看囉,這裡就不多說了。

您也可以去看您的udevd是否有在運行。
# ps aux | grep udevd
root       315  0.0  0.0   2880  1132 ?        S<s  09:35   0:00 udevd --daemon
這就是啦。

當驅動程式載入時,udevd daemon會偵測到這個事件,而後去檢查/sys目錄,
如果驅動程式建立了dev檔案的話,檔案裡會含有major及minor number,如此 udevd就能以它
建立裝置檔囉。

想讓驅動程式支援 udev 的話,必須豋錄驅動程式的 class 並在 /sys/class目錄下建立驅動程式資訊。

豋記class時是用 class_creat()這個kernel 函式。
它的prototype如下:
struct class *class_create(struct module *owner, const char *name);
owner: 還記得嗎? 就是 THIS_MODULE 啊
name:   就放 DRIVER_NAME 即可。

刪除豋記的 class是用 class_destroy()
void class_destroy (struct class *cls);

接著要建立 /sys/class/class 名稱/裝置名稱 這個檔案,用的是 device_create()
struct device *device_create(struct class *cls, struct device *parent,
                                    dev_t devt, void *drvdata,
                                    const char *fmt, ...)

cls:     傳入由class_creat() 傳回的 class。
parent:  是指定上層 class的時候使用的,傳入 NULL也行。
devt:    是dev檔顯示的major/minor number,也可用 MKDEV巨集指定。
drvdate: 是添加到裝置的資料,傳入 NULL也行。
fmt:     裝置檔的名稱。
2.6 要舊的核心,是用class_device_create()

如要刪除豋記的裝置,可用 device_destroy()
void device_destroy (struct class *class, dev_t devt);
2.6 要舊的核心,是用class_device_destroy()

大致上了解了之後,再來就是快樂的coding 時間囉^^
不好意思啊,又要等我一下啦。

時間一分一秒的過去............
好了,

chrdev_sys.c 程式碼如下:

/*****************************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>

#define DRIVER_NAME "chrdev_sys"
static unsigned int chrdev_sys_major = 0;
static unsigned int num_of_dev = 1;
static struct cdev chrdev_sys_cdev;
static struct class *chrdev_sys_class = NULL;

static int chrdev_sys_close(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "Call chrdev_sys_close.\n");
return 0;
}

static int chrdev_sys_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "Call chrdev_sys_open.\n");
return 0;
}

struct file_operations fops = {
.owner = THIS_MODULE,
.open = chrdev_sys_open,
.release = chrdev_sys_close,
};

static int chrdev_sys_init(void)
{
dev_t dev = MKDEV(chrdev_sys_major, 0);
int alloc_ret = 0;
int cdev_ret = 0;

alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME);
if (alloc_ret)
goto error;

chrdev_sys_major = MAJOR(dev);
cdev_init(&chrdev_sys_cdev, &fops);
cdev_ret = cdev_add(&chrdev_sys_cdev, dev, num_of_dev);
if (cdev_ret)
goto error;

chrdev_sys_class = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(chrdev_sys_class))
goto error;

device_create(chrdev_sys_class,
                      NULL,
                      MKDEV(chrdev_sys_major, 0),
                      NULL,
                      "ch_sys0");

printk(KERN_ALERT "%s driver(major number %d) installed.\n", DRIVER_NAME, chrdev_sys_major);
return 0;
error:
if (cdev_ret == 0)
cdev_del(&chrdev_sys_cdev);
if (alloc_ret == 0)
unregister_chrdev_region(dev, num_of_dev);

return -1;
}


static void chrdev_sys_exit(void)
{
dev_t dev = MKDEV(chrdev_sys_major, 0);

device_destroy(chrdev_sys_class, dev);
        class_destroy(chrdev_sys_class);

cdev_del(&chrdev_sys_cdev);
unregister_chrdev_region(dev, num_of_dev);
printk(KERN_ALERT "%s driver removed.\n", DRIVER_NAME);
}

module_init(chrdev_sys_init);
module_exit(chrdev_sys_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wang Chen Shu");
MODULE_DESCRIPTION("This is chrdev_sys module.");

/*****************************************************************************/

Makefile 如下:
/*****************************************************************************/

KDIR="/opt/linux-source-2.6.38"
PWD=$(shell pwd)

obj-m := chrdev_sys.o

all:
$(MAKE) -C ${KDIR} M=${PWD} modules
clean:
$(MAKE) -C ${KDIR} M=${PWD} clean

/*****************************************************************************/

run 一下囉^^

# ls
chrdev_sys.c  Makefile
# make
make -C "/opt/linux-source-2.6.38" M=/opt/test_driver/my_driver/chrdev_sys modules
make[1]: Entering directory `/opt/linux-source-2.6.38'
  CC [M]  /opt/test_driver/my_driver/chrdev_sys/chrdev_sys.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /opt/test_driver/my_driver/chrdev_sys/chrdev_sys.mod.o
  LD [M]  /opt/test_driver/my_driver/chrdev_sys/chrdev_sys.ko
make[1]: Leaving directory `/opt/linux-source-2.6.38'
# insmod ./chrdev_sys.ko
chrdev_sys driver(major number 250) installed.

看一下 /dev/底下是否已有產生了呢?

# ls -l /dev/ch_sys0
crw------- 1 root root 250, 0 2012-01-02 17:16 /dev/ch_sys0
沒錯吧,哈哈^^
移除看看囉。
# rmmod chrdev_sys
chrdev_sys driver removed.
# ls -l /dev/ch_sys0
ls: 無法存取 /dev/ch_sys0: 沒有此一檔案或目錄
看到了嗎,不見了。
好玩吧......


註記及聲明:
本教學,是參考Linux Device Driver Programming驅動程式設計由平田豐著的這本書。

沒有留言:

張貼留言