2012年1月1日 星期日

基礎 Linux Device Driver 驅動程式#7 (character device driver基礎_minor number)

有時候,如果有多個裝置,但想要做出不同的行為,那怎麼辦呢?
之前有介紹過 minor 吧。
minor number得由驅動程式自已去管理,如果想要不同的 minor提供不同的功能的話,
可以在當開啟裝置時,做minor的判斷。
但,如何做呢?不用擔心,接下來,我將會為各位示範實作的方法。
又是來寫code的時候啦,等我囉^^
時間又在一點一滴的流逝....
......................
不好意思,老婆大人在催了,
如有不足的話,我會再check。

chrdev_minor.c 原始碼如下:
/*****************************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define DRIVER_NAME "chrdev_minor"
static unsigned int chrdev_minor_major = 0;
static unsigned int num_of_dev = 2;
static struct cdev chrdev_minor_cdev;

ssize_t one_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk(KERN_ALERT "%s called\n", __func__);
return 0;
}

ssize_t one_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk(KERN_ALERT "%s called\n", __func__);
return 0;
}

static int one_close(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "This is %s (minor=%d)\n", __func__, iminor(inode));
return 0;
}

static int one_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "This is %s (minor=%d)\n", __func__, iminor(inode));
return 0;
}

struct file_operations one_fops = {
.open = one_open,
.release = one_close,
.read = one_read,
.write = one_write,
};

ssize_t zero_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk(KERN_ALERT "%s called\n", __func__);
return 0;
}

ssize_t zero_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
printk(KERN_ALERT "%s called\n", __func__);
return 0;
}

static int zero_close(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "This is %s (minor=%d)\n", __func__, iminor(inode));
return 0;
}

static int zero_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "This is %s (minor=%d)\n", __func__, iminor(inode));
return 0;
}

struct file_operations zero_fops = {
.open = zero_open,
.release = zero_close,
.read = zero_read,
.write = zero_write,
};

static int chrdev_minor_open(struct inode *inode, struct file *filp)
{
switch (iminor(inode)) {
case 0:
filp->f_op = &zero_fops;
break;
case 1:
filp->f_op = &one_fops;
break;
default:
return -ENXIO;
}

if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode, filp);

return 0;
}


struct file_operations fops = {
.owner = THIS_MODULE,
.open = chrdev_minor_open,
};

static int chrdev_minor_init(void)
{
dev_t dev = MKDEV(chrdev_minor_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_minor_major = MAJOR(dev);

cdev_init(&chrdev_minor_cdev, &fops);
        cdev_ret = cdev_add(&chrdev_minor_cdev, dev, num_of_dev);
        if (cdev_ret)
                goto error;

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

}

static void chrdev_minor_exit(void)
{
dev_t dev = MKDEV(chrdev_minor_major, 0);

cdev_del(&chrdev_minor_cdev);
unregister_chrdev_region(dev, num_of_dev);

printk(KERN_ALERT "%s driver removed.\n", DRIVER_NAME);
}

module_init(chrdev_minor_init);
module_exit(chrdev_minor_exit);

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

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

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

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

obj-m := chrdev_minor.o

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

/*****************************************************************************/
# ls
chrdev_minor.c  Makefile
# make
make -C "/opt/linux-source-2.6.38" M=/opt/test_driver/my_driver/chrdev_minor modules
make[1]: Entering directory `/opt/linux-source-2.6.38'
  CC [M]  /opt/test_driver/my_driver/chrdev_minor/chrdev_minor.o
/opt/test_driver/my_driver/chrdev_minor/chrdev_minor.c:44:2: warning: initialization from incompatible pointer type
/opt/test_driver/my_driver/chrdev_minor/chrdev_minor.c:75:2: warning: initialization from incompatible pointer type
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /opt/test_driver/my_driver/chrdev_minor/chrdev_minor.mod.o
  LD [M]  /opt/test_driver/my_driver/chrdev_minor/chrdev_minor.ko
make[1]: Leaving directory `/opt/linux-source-2.6.38'
# ls
chrdev_minor.c   chrdev_minor.mod.c  chrdev_minor.o  modules.order
chrdev_minor.ko  chrdev_minor.mod.o  Makefile        Module.symvers
# insmod ./chrdev_minor.ko
chrdev_minor driver(major number 250) installed.
# mknod /dev/ch_m1 c 250 0
# mknod /dev/ch_m2 c 250 1
# ls -l /dev/ch_m*
crw-r--r-- 1 root root 250, 0 2012-01-01 20:38 /dev/ch_m1
crw-r--r-- 1 root root 250, 1 2012-01-01 20:38 /dev/ch_m2
# cat /dev/ch_m1
# dmesg | tail
..........  /* 以上略過 */
This is zero_open (minor=0)
zero_read called
This is zero_close (minor=0)

# cat /dev/ch_m2
# dmesg | tail
..........  /* 以上略過 */
This is one_open (minor=1)
one_read called
This is one_close (minor=1)

各位,真不好意思囉,接下來的教學,未完待續.....

沒有留言:

張貼留言