2010年1月31日 星期日

Linux Kernel(11)- sysfs and device node


在linux kernel 2.6.x提供了sysfs,經由這樣的file-system可以告訴user-space系統有哪些裝置,而user-space的程式就可以動態的在/dev底下產生相對應的device node。

device node:
在/sys底下有一些名為"dev"的檔案,就是包含該裝置的major/minor number,比如:
# cat /sys/class/mem/zero/dev
1:5
# ls -al /dev/zero
crw-rw-rw- 1 root root 1, 5 2010-02-01 21:58 /dev/zero

所有的block device都可以在/sys/block/*/dev和/sys/block/*/*/dev找到。
所有的char device都可以在/sys/bus/*/devices/*/dev和/sys/class/*/*/dev找到。

所以一個簡易的動態device node產生程式就可以用script撰寫如下:
#!/bin/sh

# Block Device
for i in /sys/block/*/dev /sys/block/*/*/dev
do
    if [ -f $i ]
    then
        MAJOR=$(sed 's/:.*//' < $i)
        MINOR=$(sed 's/.*://' < $i)
        DEVNAME=$(echo $i | sed -e 's@/dev@@' -e 's@.*/@@')
        mknod /dev/$DEVNAME b $MAJOR $MINOR
    fi
done

# Char Device
for i in /sys/bus/*/devices/*/dev和/sys/class/*/*/dev
do
    if [ -f $i ]
    then
        MAJOR=$(sed 's/:.*//' < $i)
        MINOR=$(sed 's/.*://' < $i)
        DEVNAME=$(echo $i | sed -e 's@/dev@@' -e 's@.*/@@')
        mknod /dev/$DEVNAME c $MAJOR $MINOR
    fi
done



2010年1月27日 星期三

Linux Kernel(10.3)- Command line partition table parsing


MTD Partition除了在code中寫死以外,其實還可以透過一些parsers來作規劃,這一章就要來教大家如何使用"Command line partition table parsing"。首先必須在kernel中啟用"Command line partition table parsing",請參照下圖。

這樣kernel就可以支援"Command line partition table parsing",然後我們還是拿mtdram.c的code來改(紅色的部份)。

/*
 * mtdram - a test mtd device
 * Author: Alexander Larsson <alex@cendio.se>
 *
 * Copyright (c) 1999 Alexander Larsson alex@cendio.se>
 * Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de>
 *
 * This code is GPL
 *
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/mtdram.h>
#include <linux/mtd/partitions.h>

static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE;
#define MTDRAM_TOTAL_SIZE (total_size * 1024)
#define MTDRAM_ERASE_SIZE (erase_size * 1024)

#ifdef MODULE
module_param(total_size, ulong, 0);
MODULE_PARM_DESC(total_size, "Total device size in KiB");
module_param(erase_size, ulong, 0);
MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
#endif

// We could store these in the mtd structure, but we only support 1 device..
static struct mtd_info *mtd_info;
static char partitioned = 0;

static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    if (instr->addr + instr->len > mtd->size)
        return -EINVAL;

    memset((char *)mtd->priv + instr->addr, 0xff, instr->len);

    instr->state = MTD_ERASE_DONE;
    mtd_erase_callback(instr);

    return 0;
}

static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, void **virt, resource_size_t *phys)
{
    if (from + len > mtd->size)
        return -EINVAL;

    /* can we return a physical address with this driver? */
    if (phys)
        return -EINVAL;

    *virt = mtd->priv + from;
    *retlen = len;
    return 0;
}

static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
}

/*
 * Allow NOMMU mmap() to directly map the device (if not NULL)
 * - return the address to which the offset maps
 * - return -ENOSYS to indicate refusal to do the mapping
 */
static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
                       unsigned long len,
                       unsigned long offset,
                       unsigned long flags)
{
    return (unsigned long) mtd->priv + offset;
}

static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, u_char *buf)
{
    if (from + len > mtd->size)
        return -EINVAL;

    memcpy(buf, mtd->priv + from, len);

    *retlen = len;
    return 0;
}

static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t *retlen, const u_char *buf)
{
    if (to + len > mtd->size)
        return -EINVAL;

    memcpy((char *)mtd->priv + to, buf, len);

    *retlen = len;
    return 0;
}

static void __exit cleanup_mtdram(void)
{
    if (mtd_info) {

        if (mtd_has_partitions() && partitioned) {
            del_mtd_partitions(mtd_info);
        } else {
            del_mtd_device(mtd_info);
        }

        vfree(mtd_info->priv);
        kfree(mtd_info);
    }
}

int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
        unsigned long size, char *name)
{
    memset(mtd, 0, sizeof(*mtd));

    /* Setup the MTD structure */
    mtd->name = name;
    mtd->type = MTD_RAM;
    mtd->flags = MTD_CAP_RAM;
    mtd->size = size;
    mtd->writesize = 1;
    mtd->erasesize = MTDRAM_ERASE_SIZE;
    mtd->priv = mapped_address;

    mtd->owner = THIS_MODULE;
    mtd->erase = ram_erase;
    mtd->point = ram_point;
    mtd->unpoint = ram_unpoint;
    mtd->get_unmapped_area = ram_get_unmapped_area;
    mtd->read = ram_read;
    mtd->write = ram_write;

    if (mtd_has_partitions()) {
        struct mtd_partition *mtd_parts = NULL;
        static const char *probes[] =
                    { "cmdlinepart", NULL };
        int nb_parts = 0;
        printk("has partitions\n");
        if (mtd_has_cmdlinepart()) {
            printk("has probs\n");
            nb_parts = parse_mtd_partitions(mtd, probes, &mtd_parts, 0);
        }
        if (nb_parts > 0) {
            printk("partitioned\n");
            partitioned = 1;
            return add_mtd_partitions(mtd, mtd_parts, nb_parts);
        }
    }

    if (add_mtd_device(mtd)) {
        return -EIO;
    }

    return 0;
}

static int __init init_mtdram(void)
{
    void *addr;
    int err;

    if (!total_size)
        return -EINVAL;

    /* Allocate some memory */
    mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
    if (!mtd_info)
        return -ENOMEM;

    addr = vmalloc(MTDRAM_TOTAL_SIZE);
    if (!addr) {
        kfree(mtd_info);
        mtd_info = NULL;
        return -ENOMEM;
    }
    err = mtdram_init_device(mtd_info, 
                  addr, MTDRAM_TOTAL_SIZE, "brook_flash");
    if (err) {
        vfree(addr);
        kfree(mtd_info);
        mtd_info = NULL;
        return err;
    }
    memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
    return err;
}

module_init(init_mtdram);
module_exit(cleanup_mtdram);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>");
MODULE_DESCRIPTION("Simulated MTD driver for testing");




Linux Kernel(10.2)- mtd partitions


在drivers/mtd/devices/mtdram.c中可以知道,透過add_mtd_device()/del_mtd_device()新增和刪除mtd device,但是並沒有規劃partition,在這邊將介紹add_mtd_partitions()/del_mtd_partitions()讓您可以透過這兩個函數為mtd切割partition。
此範例為drivers/mtd/devices/mtdram.c內容來修改而成(紅色粗體為修改部份)。
/*
 * mtdram - a test mtd device
 * Author: Alexander Larsson 
 *
 * Copyright (c) 1999 Alexander Larsson 
 * Copyright (c) 2005 Joern Engel 
 *
 * This code is GPL
 *
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/mtdram.h>
#include <linux/mtd/partitions.h>

static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE;
#define MTDRAM_TOTAL_SIZE (total_size * 1024)
#define MTDRAM_ERASE_SIZE (erase_size * 1024)

static struct mtd_partition brook_partitions[] = {
    {
        .name = "part-1",
        .size = 0x00100000,
        .offset = 0x0000000
    }, {
        .name = "part-2",
        .size = 0x00100000,
        .offset = MTDPART_OFS_APPEND,
        .mask_flags = MTD_WRITEABLE
    }, {
        .name = "part-3",
        .offset = MTDPART_OFS_APPEND,
    },
};

#ifdef MODULE
module_param(total_size, ulong, 0);
MODULE_PARM_DESC(total_size, "Total device size in KiB");
module_param(erase_size, ulong, 0);
MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
#endif

// We could store these in the mtd structure, but we only support 1 device..
static struct mtd_info *mtd_info;

static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
    if (instr->addr + instr->len > mtd->size)
        return -EINVAL;

    memset((char *)mtd->priv + instr->addr, 0xff, instr->len);

    instr->state = MTD_ERASE_DONE;
    mtd_erase_callback(instr);

    return 0;
}

static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, void **virt, resource_size_t *phys)
{
    if (from + len > mtd->size)
        return -EINVAL;

    /* can we return a physical address with this driver? */
    if (phys)
        return -EINVAL;

    *virt = mtd->priv + from;
    *retlen = len;。
    return 0;
}

static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
}

/*
 * Allow NOMMU mmap() to directly map the device (if not NULL)
 * - return the address to which the offset maps
 * - return -ENOSYS to indicate refusal to do the mapping
 */
static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
                       unsigned long len,
                       unsigned long offset,
                       unsigned long flags)
{
    return (unsigned long) mtd->priv + offset;
}

static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
        size_t *retlen, u_char *buf)
{
    if (from + len > mtd->size)
        return -EINVAL;

    memcpy(buf, mtd->priv + from, len);

    *retlen = len;
    return 0;
}

static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
        size_t *retlen, const u_char *buf)
{
    if (to + len > mtd->size)
        return -EINVAL;

    memcpy((char *)mtd->priv + to, buf, len);

    *retlen = len;
    return 0;
}

static void __exit cleanup_mtdram(void)
{
    if (mtd_info) {
        del_mtd_partitions(mtd_info);
        vfree(mtd_info->priv);
        kfree(mtd_info);
    }
}

int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
        unsigned long size, char *name)
{
    memset(mtd, 0, sizeof(*mtd));

    /* Setup the MTD structure */
    mtd->name = name;
    mtd->type = MTD_RAM;
    mtd->flags = MTD_CAP_RAM;
    mtd->size = size;
    mtd->writesize = 1;
    mtd->erasesize = MTDRAM_ERASE_SIZE;
    mtd->priv = mapped_address;

    mtd->owner = THIS_MODULE;
    mtd->erase = ram_erase;
    mtd->point = ram_point;
    mtd->unpoint = ram_unpoint;
    mtd->get_unmapped_area = ram_get_unmapped_area;
    mtd->read = ram_read;
    mtd->write = ram_write;

    if (add_mtd_partitions(mtd, brook_partitions,
                ARRAY_SIZE(brook_partitions))) {
        return -EIO;
    }

    return 0;
}

static int __init init_mtdram(void)
{
    void *addr;
    int err;

    if (!total_size)
        return -EINVAL;

    /* Allocate some memory */
    mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
    if (!mtd_info)
        return -ENOMEM;

    addr = vmalloc(MTDRAM_TOTAL_SIZE);
    if (!addr) {
        kfree(mtd_info);
        mtd_info = NULL;
        return -ENOMEM;
    }
    err = mtdram_init_device(mtd_info, addr, 
                  MTDRAM_TOTAL_SIZE, "brook_flash");
    if (err) {
        vfree(addr);
        kfree(mtd_info);
        mtd_info = NULL;
        return err;
    }
    memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
    return err;
}

module_init(init_mtdram);
module_exit(cleanup_mtdram);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Larsson >alexl@redhat.com>");
MODULE_DESCRIPTION("Simulated MTD driver for testing");
如果要切割partitions就必須要提供partitions的資訊,於是我們就宣告了一個名為brook_partitions的static struct mtd_partition,然後將原本呼叫add_mtd_device()/del_mtd_device()分別改成add_mtd_partitions()/del_mtd_partitions()就大功告成了。




熱門文章