2010年1月31日 星期日

Linux Kernel(11.1)- sysfs and hotplug


Linux提供兩種非同步的hotplug機制通知user-space裝置狀態的改變,一是usermode helper,另一個則是透過netlink。

usermode helper
每當kernel收到hotplug event便會執行"CONFIG_UEVENT_HELPER_PATH"(預設值是/sbin/hotplug,可以透過修改/proc/sys/kernel/hotplug修改預設值),在embedded system中,常用的是busybox,所以,通常會把UEVENT HELPER換成/sbin/mdev(找時間來寫寫busybox),執行UEVENT HELPER會攜帶一些環境變數,如ACTION/DEVPATH/SUBSYSTEM/HOME/PATH等等,透過這些環境變數,可以取得Device Name,MAJOR/MINOR number等等,我們先來看部份的程式碼,往後有機會在作深度研究。
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
    ... 略...
    /* call uevent_helper, usually only enabled during early boot */
    if (uevent_helper[0]) {
        char *argv [3];

        argv [0] = uevent_helper;
        argv [1] = (char *)subsystem;
        argv [2] = NULL;
        retval = add_uevent_var(env, "HOME=/");
        if (retval)
            goto exit;
        retval = add_uevent_var(env,
                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
        if (retval)
            goto exit;

        retval = call_usermodehelper(argv[0], argv,
                        env->envp, UMH_WAIT_EXEC);
    }
    ... 略...
}

我利用一個shell script來取代UEVENT HELPER,主要目的是要印出有哪些環境變數,和傳遞哪些參數給UEVENT HELPER。
#!/bin/sh
env >> /helper.log
echo "CMD:" $@ >> /helper.log
echo "----------     end     ----------" >> /helper.log



netlink
另外一條路徑就是netlink了,基本上也是傳遞相同的資訊。
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
    ... 略...
#if defined(CONFIG_NET)
    /* send netlink message */
    if (uevent_sock) {
        struct sk_buff *skb;
        size_t len;

        /* allocate message with the maximum possible size */
        len = strlen(action_string) + strlen(devpath) + 2;
        skb = alloc_skb(len + env->buflen, GFP_KERNEL);
        if (skb) {
            char *scratch;

            /* add header */
            scratch = skb_put(skb, len);
            sprintf(scratch, "%s@%s", action_string, devpath);

            /* copy keys to our continuous event payload buffer */
            for (i = 0; i < env->envp_idx; i++) {
                len = strlen(env->envp[i]) + 1;
                scratch = skb_put(skb, len);
                strcpy(scratch, env->envp[i]);
            }

            NETLINK_CB(skb).dst_group = 1;
            retval = netlink_broadcast(uevent_sock, skb, 0, 1,
                            GFP_KERNEL);
            /* ENOBUFS should be handled in userspace */
            if (retval == -ENOBUFS) {
                retval = 0;
            }
        } else {
            retval = -ENOMEM;
        }
    }
#endif
    ... 略...
}

user-space的code如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <linux/types.h>
#include <linux/netlink.h>

int main(int argc, char *argv[])
{
    struct sockaddr_nl nls;
    struct pollfd pfd;
    char buf[512];

    memset(&nls, 0, sizeof(nls));
    nls.nl_family = AF_NETLINK;
    nls.nl_pid = getpid();
    nls.nl_groups = -1;

    pfd.events = POLLIN;
    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (pfd.fd == -1) {
        printf("Not root\n");
        exit(1);
    }

    if (bind(pfd.fd, (void*)&nls, sizeof(nls))) {
        printf("bind failed\n");
        exit(1);
    }
    while (-1 != poll(&pfd, 1, -1)) {
        int i, len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
        if (len == -1) {
            printf("recv\n");
            exit(1);
        }
        i = 0;
        while (i < len) {
            printf("%s\n", buf + i);
            i += strlen(buf+i) + 1;
        }
    }
    printf("\n");
    return 0;
}




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()就大功告成了。




2010年1月18日 星期一

Linux Kernel(10.1)- drivers/mtd/devices/mtdram.c


MTD的基本介紹可以參考MTD - Memory Technology Devices,這篇文章要透過drivers/mtd/devices/mtdram.c來了解mtd的driver如何運作。
static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
#define MTDRAM_TOTAL_SIZE (total_size * 1024)

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

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, "mtdram test device");
 if (err) {
  vfree(addr);
  kfree(mtd_info);
  mtd_info = NULL;
  return err;
 }
 memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
 return err;
}
首先,看到init_mtdram(),在該function中,我們分配一塊記憶體給mtd_info,以及一塊v-memory給稍候模擬的flash用,接著就呼叫mtdram_init_device()進行mtd的註冊動作,(一個mtd partition需要一個mtd_info來存放所需的資訊),init_mtdram()後面就將mtd_info->priv(即v-memory)的內容全部設成0xff,這是因為一個空的flash裡面預設就是0xff。

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_device(mtd)) {
  return -EIO;
 }

 return 0;
}
在mtdram_init_device()主要是填mtd_info相關資訊,然後呼叫add_mtd_device()進行註冊mtd的動作。呼叫del_mtd_device()進行移除mtd的工作。
point()/unpoint()可以參考http://www.linux-mtd.infradead.org/faq/general.html#L_point。

#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;
}
剩下的read()/write()/erase()都是copy from/to memory和清成0xff,所以您可以發現讀寫NOR flash和讀寫memory差不多。



2010年1月16日 星期六

Linux Kernel(10)- MTD - Memory Technology Devices


說到MTD您就不得不親自拜訪一下MTD的官網(http://www.linux-mtd.infradead.org/),傳統上UNIX將device分成兩大類,char device和block device,char device就像鍵盤,可以讀資料,但卻不能做seek,也沒有固定大小,而block就像硬碟一樣,可以隨機存取某個位置(seek)。而MTD並不是char device也不是block device,因此建立了新的device類別,稱為MTD。
MTD subsystem提供一個抽象層(FTL)來存取flash device(如NAN、OneNAND、NOR等等),而我們一般用的USB flash因為有IC控制,以Linux的角度看起來就像block device,而不是一個原生的(raw) flash。

一般PC都不會接這些raw flash,不過我們可以透過一些simulate來練習這些device。
在insmod mtd.ko之後我可以透過/proc/mtd得知目前有哪些MTD,因為我們系統當然沒有MTD的device,所以可以insmod mtdram.ko安裝一個虛擬的MTD。

如果要能mount raw flash,還必須透過block device的介面存取,所以在安裝一下mtdblock.ko吧。

利用,mkfs.jffs2建立一個jffs2的image,再利用flashcp將image燒錄到flash中,最後就可以mount來用啦。

這一張圖是利用dd將flash的資料備份下來,再利用flashcp還原資料。



ubuntu package - pppoeconf


在Ubuntu上的NetworkManager(0.7.996)似乎對於xDSL連線還是有些問題,不過還是可以透過pppoeconf這個古老的設定程式進行設定連線,下面就貼一些圖供大家參考嚕。

顯示pppoeconf找到的網路介面,要注意的是看您的pppoe介面是否有在這個list中。





如果你的帳號或者連線的介面卡有變更,也是可以修改這個檔案的。



輸入帳號。



密碼。



確認真的有取得IP。

一旦將interface在/etc/network/interfaces中作設定,NetworkManager就不會作管理,所以,設定完我還是把dsl-provider自/etc/network/interfaces移除,然後透過pon/poff/plog操作我的dsl連線(個人特殊的環境的小改變啦)。


2010年1月14日 星期四

Jesse Livermore語錄(2)


在jesses livermore的第二章中,我看到了耐心,投機會讓人窮忙個不停,在你什麼也不做的時候,那些自覺必須要每天殺進殺出的投機客,正在為你的下一趟冒險之旅打基礎,你將自他們所犯的錯誤中歡呼收割。
不要執著於每天那上上下下的小小變動,而是要抓對趨勢,賺進大波段,每當你不能確定是否應該進場時,慢一點無妨,因為真正的行情不會只有一兩天。

而第三章,"追隨主流股",每一個時期都會有主流股,十幾年前的金融,到現在的電子,IC等等,主流股就像流行一樣,吸引多數人的目光,市場願意給比較高的"本夢比",如果你不能從主流股中賺到錢,那麼,你就更難從其他類股中賺到錢了,主流也會退潮流,當他由千元關卡跌下來,不見得下一次他還能上千元關卡,而且很可能從此一路向下,培養敏銳的觀察力,發掘下一波的主流股吧。
"Speculation is far too exciting. Most people who speculate hound the brokerage offices... the ticker is always on their minds. They are so engrossed with the minor ups and downs, they miss the big movements."

第四章,教你如何運用你手中的錢,多數的人往往使用的是攤平損失,但是,如果你看錯了,攤平損失將會造成更大的損失,大師的做法是一但看錯,立刻出場,如果看對了,就一路加碼。
第五章,"關鍵點",Jesse Livermore的操盤是結合時間+資金管理+情緒管理,在適當的時間點介入,並在訊號出現時出場,每個重要的走勢,不過是重覆以往的價格變動,只要能熟悉過去的走勢,就能預測未來,並採取正確的行動。
"The point is not so much to buy as cheap as possible or go short at top price, but to buy or sell at the right time."

"...the fruits of your success will be in direct ratio to the honesty and sincerity of your own effort in keeping your own records, doing your own thinking, and reaching your own conclusions."


2010年1月11日 星期一

ExtJS應用程式設計原則


取自http://blog.joomla.org.tw/javascript/53-extjs/71-extjs-application-design-1.html

作者是 EddyChang

這篇文章是從ExtJS官網討論區中找到的,非常值得參考,原來的問題是:「application design, component creation and efficiency」。由我個人的觀點來看,Javascript發展至今,仍然是百家爭鳴的時代,有非常多不同的framework或pattern,也很難和各式的應用能完全配合,ExtJS算是非常豐富的Javascript架構,但還是要配合像PHP的Server端程式才能完成整個應用程式。以下是關於Saki(ExtJS官方支援小組)的經驗回答摘譯:

使用以下的規則在開發Ext大型的應用程式上:
  1. 盡可能地多使用緩慢實例化(xtype)  - Use lazy instantiation (xtype) as much as possible.
  2. 使用預先設定好的類別 - Use pre-configured classes (I'll explain later).
  3. 在父階層中實作關係  - Implement relations on parent level.
  4. 在開發時,保持每個類別在自己的一個檔案;在產品化時,再組合和壓縮它們  - Keep each class in its own file while developing, concat and minify for production.

1. 盡可能地多使用緩慢實例化(xtype)

這點是如果你使用xtypes的話,Ext物件只有當他們需要時才會被建立。

備註:xtype和延伸類別的分離方式,的確是對大型開發有幫助。但關於上面的說明,在討論區的另一篇:「Does xtype really support lazy instantiation?」提出了lazy instantiation的質疑,以及和lazy redener的比較。


2. 使用預先設定好的類別

上述第1點的xtype的方法要配合預先設定好的類別"pre-configured classes",這些類別是擴充自Ext的類別而來的,帶有設定選項和(或)加入的函式。

備註:有許多範例可以了解怎麼寫出預先設定好的擴充類別,例如以下的: 3.在父階層中實作關係

想像你有一個border版面中,在西(左)邊有一個表格和中間有一個表單,當選了表格中的某個項目時,表單中要顯示對應的值。那到底要怎麼寫這段程式邏輯,是放在表格裡,還是表單裡?應該都不是這兩個。這兩者互不知道對方存在,知道這兩者同時存在的是它們的父階層(可能是viewpoint或window)

因此,關係會建立在父階層裡,例如window中。這裡的程式會是監聽表格來的事件,然後在選擇改變時載入表單資料,或是當表單進出資料後,改變表格的記錄。

如果我把程式碼寫在表格裡,那這個表格就和表單不可分離了。

備註:的確是很好的原則,不過直接的想法都是寫到事件發動處,在小型的事件處理,這樣比較快而且直觀。Saki在它的範例網站中,有加了兩個範例,這兩個範例都是很好的學習資源: 4.在開發時,保持每個類別在自己的一個檔案;在產品化時,再組合和壓縮它們

心得:ExtJS原本的作法就是如此,Linux下可以用Cat指令,Windows下可用ConCat/Split的軟體來合併Javascript檔案,再利用JSBuilder之類的工具,可以去除註解檔和壓縮檔案。

最後的忠告
不需要太深思熟慮於程式的結構、版面、各種控制器、載入器、介面…太多了。而是要寫出好的可重覆利用的預先設定好的類別,然後把它們不管如何先放到一起。如果這些類別真的是不錯而且可以重覆使用的,你大可以更改你的應用程式版本,使用別的方式來作,但你的類別至少仍然都會正常工作。就像是樂高積木一樣-如果你有木塊,你可以在幾分鐘內建出一個城堡。

心得:的確是如此。預先設定好的類別是必學的一段,雖然我有看過另一種寫法 - Module Pattern,Module Pattern是通用於各Javascript框架的寫法,或許也是一個值得一學的部份。在官方討論區中的「preconfigured class vs. module pattern」一文中有一些比較資訊可以參考。


2010年1月7日 星期四

Jesse Livermore語錄(1)


"There is nothing new in Wall Street. There can't be because speculation is as old as the hills. Whatever happens in the stock market today has happened before and will happen again."
華爾街未曾改變,財富來來去去,股票起起落落,但華爾街永遠沒變,因為人性永遠不會改變。
相信大家從去年的3955到去年的最後一天8188有著很深的感觸吧,當跌到谷底的時候,即便你想再跌也差不多了,就是不敢進去,到了年底,想說再漲應該也有限了吧(即便上萬,也只剩不到2000點),但是,在3955的時候,每個人心中只有恐懼,敢買的人有限,敢一口氣抱到年底的更是少數(電子股很多都漲超過5倍了),到了年底,8188之上,每個人的貪婪與無知,將自己推往股市,人性真的沒變過阿。
不斷的重複著貪婪、恐懼、無知、希望,所以市場重複著相同的結構與步驟,這一切的一切都是源自於人性

每個人都知道低買高賣,但是殺到後來你敢買嗎?
每個人都知道不可追高殺低,但是漲到你忍不住,你還是買了
耐心等到,是賺錢的不二法則,卻也是一種高深的修養


2010年1月5日 星期二

Linux Kernel(9)- Kthread


在kernel中建立thread可以使用kthread_create(),建立一個task,然後在調用wake_up_process(task)讓task真正的運行,如果要kill一個kthread可以使用kthread_stop()。
在kernel中,將kthread_create()和wake_up_process()包裝成kthread_run(),也就是調用了kthread_run()之後,該thread會立刻被執行。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>

MODULE_LICENSE("GPL");

static struct task_struct *brook_tsk;
static int data;
static int kbrook(void *arg);

static int kbrook(void *arg)
{
    unsigned int timeout;
    int *d = (int *) arg;

    for(;;) {
        if (kthread_should_stop()) break;
        printk("%s(): %d\n", __FUNCTION__, (*d)++);
        do {
            set_current_state(TASK_INTERRUPTIBLE);
            timeout = schedule_timeout(10 * HZ);
        } while(timeout);
    }
    printk("break\n");

    return 0;
}

static int __init init_modules(void)
{
    int ret;

    brook_tsk = kthread_create(kbrook, &data, "brook");
    if (IS_ERR(brook_tsk)) {
        ret = PTR_ERR(brook_tsk);
        brook_tsk = NULL;
        goto out;
    }
    wake_up_process(brook_tsk);

    return 0;

out:
    return ret;
}

static void __exit exit_modules(void)
{
    kthread_stop(brook_tsk);
}

module_init(init_modules);
module_exit(exit_modules);


linux/kthread.h
/**
 * kthread_run - create and wake a thread.
 * @threadfn: the function to run until signal_pending(current).
 * @data: data ptr for @threadfn.
 * @namefmt: printf-style name for the thread.
 *
 * Description: Convenient wrapper for kthread_create() followed by
 * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM).
 */
#define kthread_run(threadfn, data, namefmt, ...)      \
({            \
 struct task_struct *__k         \
  = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
 if (!IS_ERR(__k))         \
  wake_up_process(__k);        \
 __k;           \
})


2010年1月4日 星期一

ubuntu package - bind9-host


host主要拿來作DNS的lookup,和nslookup一樣,可以作正解(lookup/由domain name找到IP),也可以作反解(reverse lookup/由IP找到domain name),大多數的User比較常接觸到的是正解。
brook@ubuntu:~$ host dns.hinet.net
dns.hinet.net has address 168.95.1.1
brook@ubuntu:~$ 
brook@ubuntu:~$ host 168.95.1.1
1.1.95.168.in-addr.arpa domain name pointer dns.hinet.net.
brook@ubuntu:~$ 
brook@ubuntu:~$ host www.google.com
www.google.com is an alias for www.l.google.com.
www.l.google.com has address 72.14.203.99
www.l.google.com has address 72.14.203.147
www.l.google.com has address 72.14.203.104
www.l.google.com has address 72.14.203.103
www.l.google.com has address 72.14.203.105
www.l.google.com has address 72.14.203.106
brook@ubuntu:~$ 
brook@ubuntu:~$ host 72.14.203.99
99.203.14.72.in-addr.arpa domain name pointer tx-in-f99.1e100.net.

建議參考資料︰
  http://linux.vbird.org/linux_server/0350dns.php
  http://plog.longwin.com.tw/news_security/2005/11/06/dns_error_top_3

2010年1月3日 星期日

Linux Kernel(8)- Notification


Kernel提供一個notifiers/notifier chains的機制,這是publish-and-subscribe的機制,也就是需要的人自己去訂閱(join到某個notifier chain中),當這個chain的provider有事件要發布,就publish出來(發布給join這個chain的所有人)。
kernel中也提供了一些notifier,如reboot,可以透過register_reboot_notifier()訂閱,用unregister_reboot_notifier()取消訂閱。我們也可以自訂自己的notifier,以下例子就是自訂一個notifier。此一範例為透過寫入/proc/brook_notifier將資料publish給訂閱brook_notifier_list的scbscriber

notifier publisher
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>

#include "notifier.h"

MODULE_LICENSE("GPL");
// 宣告一個新的notifier list – brook_notifier_list
BLOCKING_NOTIFIER_HEAD(brook_notifier_list);

// 訂閱brook_notifier_list事件的wrapper function
int register_brook_notifier(struct notifier_block *nb)
{
    return blocking_notifier_chain_register(&brook_notifier_list, nb);
}
EXPORT_SYMBOL(register_brook_notifier);

// 取消訂閱brook_notifier_list事件的wrapper function
int unregister_brook_notifier(struct notifier_block *nb)
{
    return blocking_notifier_chain_unregister(&brook_notifier_list, nb);
}
EXPORT_SYMBOL(unregister_brook_notifier);

// procfs的write function
static int write_proc(struct file *filp, const char __user *buf,
                               unsigned long count, void *data)
{
    char *p = kzalloc(sizeof(char) * count, GFP_KERNEL);
    if (!p) {
        printk("no mem\n");
        return -ENOMEM;
    }
    if (copy_from_user(p, buf, count)) {
        printk("fault\n");
        return -EFAULT;
    }
    printk("%s(): msg=\"%s\"\n", __FUNCTION__, p);

    // 將事件published給brook_notifier_list的subscriber
    blocking_notifier_call_chain(&brook_notifier_list, brook_num1, (void*)p);
    kfree(p);
    return count;
}

static int __init init_modules(void)
{
    struct proc_dir_entry *ent;

    ent = create_proc_entry("brook_notifier", S_IFREG | S_IWUSR, NULL);
    if (!ent) {
        printk("create proc child failed\n");
    } else {
        ent->write_proc = write_proc;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    remove_proc_entry("brook_notifier", NULL);
}

module_init(init_modules);
module_exit(exit_modules);


notifier subscriber
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>

#include "notifier.h"

MODULE_LICENSE("GPL");

// callback function, 當brook_notifier_list有事件發生時, 會呼叫該function
static int brook_notify_sys(struct notifier_block *this,
                            unsigned long code, void *data)
{
    printk("%s(): code=%ld, msg=\"%s\"\n", __FUNCTION__, code, (char*)data);
    return 0;
}

// 宣告要註冊到brook_notifier_list的struct
static struct notifier_block brook_notifier = {
        .notifier_call =    brook_notify_sys,
};

static int __init init_modules(void)
{
    // 將brook_notifier註冊到brook_notifier_list
    register_brook_notifier(&brook_notifier);
    return 0;
}

static void __exit exit_modules(void)
{
    // 將brook_notifier自brook_notifier_list移除
    unregister_brook_notifier(&brook_notifier);
}

module_init(init_modules);
module_exit(exit_modules);


header file
#ifndef BROOK_NOTIFIER_H
#define BROOK_NOTIFIER_H

#include <linux/notifier.h>

int register_brook_notifier(struct notifier_block *nb);
int unregister_brook_notifier(struct notifier_block *nb);

// event type
enum brook_msg {
    brook_num1,
    brook_num2,
    brook_num3
};

#endif
基本上我們都透過notifier_chain_register()來訂閱某個notifier,透過notifier_chain_unregister()取消某個notifier的訂閱,用notifier_call_chain()來發布event,不過我們常常會用對訂閱與取消訂閱寫一層wrapper,如我們的register_brook_notifier()/unregister_brook_notifier()。



參考資料:



熱門文章