2009年11月26日 星期四

Linux Kernel(3)- procfs


(III)將和大家介紹procfs,雖然後來procfs已經失控,也不再建議大家使用,但是還是有一定的用途,所以,有興趣的人可以一起探討一下。

/proc是一個特殊的檔案系統,當讀取/proc底下的檔案時,其內容由kernel動態產生,很多應用程式也都是存取/proc底下的檔案,我們藉由兩個範例來說明。第一個用於當/proc只提供read-only的時候,第二個用於/proc提供具有write功能的時候。

Read-Only

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* read file operations */
static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

/* private data */
static uint32_t *lines;

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

    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    /* create a procfs entry for read-only */
    ent = create_proc_read_entry ("brook", S_IRUGO, NULL, read_proc, lines);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    /* remove procfs entry */
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

在這個例子中我們使用一個globa variable "lines",用於存放上次列印到第幾個數值,MAX_LINE是上限。一開始載入module就先分配記憶體給lines,接著呼叫create_proc_read_entry()建立一個唯讀的procfs檔案。

static inline struct proc_dir_entry *
    create_proc_read_entry(const char *name,
        mode_t mode, struct proc_dir_entry *base,
        read_proc_t *read_proc, void * data);


name是proc底下的檔案名稱。mode是檔案的mode,可以直接用0666的表示法表示,如果是建立目錄則要or S_IFDIR。base如果為null,則會把/proc當根目錄,可以是其他的proc_dir_entry pointer,那麼就會以proc_dir_entry當根目錄開始往下長。read_proc則是當user讀取該檔案時的call-back function。data則用於存放額外資訊的地方,可以先alloc,然後每次執行read時,都可以拿出來使用。

typedef int (read_proc_t)(char *page, char **start, off_t off,
    int count, int *eof, void *data);

page是系統給的buffer,也是我們要寫入的地方,寫到buffer裡面的資料,會被當成proc的內容。start如果只要一次就可以read完,可以忽略start,否則只要將page設定給start就可以了。count則是user-space想要讀取的資料量。offset指示目前檔案位置。eof指示是否已經讀到結尾。data用於存放額外的資訊。


Writable

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

MODULE_LICENSE("GPL");

#define MAX_LINE        1000

/* write file operations */
static int write_proc(struct file *file, const char __user *buf,
                       unsigned long count, void *data)
{
    char num[10], *p;
    int nr, len;

    /* no data be written */
    if (!count) {
        printk("count is 0\n");
        return 0;
    }

    /* Input size is too large to write our buffer(num) */
    if (count > (sizeof(num) - 1)) {
        printk("input is too large\n");
        return -EINVAL;
    }

    if (copy_from_user(num, buf, count)) {
        printk("copy from user failed\n");
        return -EFAULT;
    }

    /* atoi() */
    p = num;
    len = count;
    nr = 0;
    do {
        unsigned int c = *p - '0';
        if (*p == '\n') {
            break;
        }
        if (c > 9) {
            printk("%c is not digital\n", *p);
            return -EINVAL;
        }
        nr = nr * 10 + c;
        p++;
    } while (--len);
    *(uint32_t*) data = nr;

    return count;
}

static int read_proc(char *page, char **start, off_t off,
                       int count, int *eof, void *data)
{
    uint32_t curline;
    char *p, *np;

    *start = p = np = page;
    curline = *(uint32_t*) data;

    printk("count=%d, off=%ld\n", count, off);
    for (;curline < MAX_LINE && (p - page) < count; curline++) {
        np += sprintf(p, "Line #%d: This is Brook's demo\n", curline);
        if ((count - (np - page)) < (np - p)) {
            break;
        }
        p = np;
    }

    if (curline < MAX_LINE) {
        *eof = 1;
    }
    *(uint32_t*)data = curline;
    return (p - page);
}

static uint32_t *lines;

static int __init init_modules(void)
{

    struct proc_dir_entry *ent;
    lines = kzalloc(sizeof(uint32_t), GFP_KERNEL);
    if (!lines) {
        printk("no mem\n");
        return -ENOMEM;
    }
    *lines = 0;

    ent = create_proc_entry("brook", S_IFREG | S_IRWXU, NULL);
    if (!ent) {
        printk("create proc failed\n");
        kfree(lines);
    } else {
        ent->write_proc = write_proc;
        ent->read_proc = read_proc;
        ent->data = lines;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    if (lines) {
        kfree(lines);
    }
    remove_proc_entry("brook", NULL);
}

module_init(init_modules);
module_exit(exit_modules);

第二個範例則提供了write的功能,所以不能直接使用create_proc_read_entry()建立procfs檔案,而是要使用create_proc_entry()建立procfs檔案,並且設定read/write file operations,以及"data"。該範例中的write是設定line。

typedef int (write_proc_t)(struct file *file, const char __user *buffer,
    unsigned long count, void *data);




2009年11月25日 星期三

Linux Kernel(2)- register char device


II我們將介紹如何向Linux註冊char device,並且read/write該device。
#include <linux/init.h>
#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h> // chrdev
#include <linux/cdev.h> // cdev_add()/cdev_del()
#include <asm/uaccess.h> // copy_*_user()

MODULE_LICENSE("GPL");

#define DEV_BUFSIZE 1024

int dev_major;
int dev_minor;
struct cdev *dev_cdevp = NULL;

int dev_open(struct inode*, struct file*);
int dev_release(struct inode*, struct file*);
ssize_t dev_read(struct file*, char __user*, size_t, loff_t*);
ssize_t dev_write(struct file*, const char __user *, size_t, loff_t*);
static void __exit exit_modules(void);

struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = dev_open,
    .release = dev_release,
    .read = dev_read,
    .write = dev_write,
};

int dev_open(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

int dev_release(struct inode *inode, struct file *filp)
{
    printk("%s():\n", __FUNCTION__);
    return 0;
}

ssize_t 
dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    char data[] = "brook";
    ssize_t ret = 0;

    printk("%s():\n", __FUNCTION__);

    if (*f_pos >= sizeof(data)) {
        goto out;
    }

    if (count > sizeof(data)) {
        count = sizeof(data);
    }

    if (copy_to_user(buf, data, count) < 0) {
        ret = -EFAULT;
        goto out;
    }
    *f_pos += count;
    ret = count;

out:
    return ret;
}

ssize_t 
dev_write(struct file *filp, const char __user *buf, size_t count,
          loff_t *f_pos)
{
    char *data;
    ssize_t ret = 0;

    printk("%s():\n", __FUNCTION__);

    data = kzalloc(sizeof(char) * DEV_BUFSIZE, GFP_KERNEL);
    if (!data) {
        return -ENOMEM;
    }

    if (count > DEV_BUFSIZE) {
        count = DEV_BUFSIZE;
    }

    if (copy_from_user(data, buf, count) < 0) {
        ret = -EFAULT;
        goto out;
    }
    printk("%s(): %s\n", __FUNCTION__, data);
    *f_pos += count;
    ret = count;
out:
    kfree(data);
    return ret;
}

static int __init init_modules(void)
{
    dev_t dev;
    int ret;

    ret = alloc_chrdev_region(&dev, 0, 1, "brook");
    if (ret) {
        printk("can't alloc chrdev\n");
        return ret;
    }

    dev_major = MAJOR(dev);
    dev_minor = MINOR(dev);
    printk("register chrdev(%d,%d)\n", dev_major, dev_minor);

    dev_cdevp = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    if (dev_cdevp == NULL) {
        printk("kzalloc failed\n");
        goto failed;
    }
    cdev_init(dev_cdevp, &dev_fops);
    dev_cdevp->owner = THIS_MODULE;
    ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor), 1);
    if (ret < 0) {
        printk("add chr dev failed\n");
        goto failed;
    }

    return 0;

failed:
    if (dev_cdevp) {
        kfree(dev_cdevp);
        dev_cdevp = NULL;
    }
    return 0;
}

static void __exit exit_modules(void)
{
    dev_t dev;

    dev = MKDEV(dev_major, dev_minor);
    if (dev_cdevp) {
        cdev_del(dev_cdevp);
        kfree(dev_cdevp);
    }
    unregister_chrdev_region(dev, 1);
    printk("unregister chrdev\n");
}

module_init(init_modules);
module_exit(exit_modules);

當module被load進來時,init_modules()會被執行,而init_modules()做了幾件事情,首先向Linux要求分配char device number "alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)"。dev是分配到的dev_t(output parameter),baseminor是起始的minor number,當baseminor=0會由系統分配,否則會試圖尋找可以符合baseminor的minor number,count是minor的數量,name是註冊的名稱。
接著設定struct cdev "cdev_init()",並且註冊到系統"cdev_add()",在cdev_init()的同時也設定了該device的file operations。

struct file_operations就是device的file operations,簡單的說,UNIX所有的東西都可以視為檔案,當然包含device,既然是檔案,就會有open、close/release、read和write等等操作,而這些操作都會觸發對應的function來反應,這就是file operations的功能了。

我們的read會回應"brook",而write則是藉由printk()印出來,open和close/release都單純的回應成功而已。

而exit_modules()當然就是負責歸還當初和OS要的資源了"kfree()",當然包含之前註冊的char device number,現在也要unregister "unregister_chrdev_region()"。




U-Boot Standalone Applications


U-Boot支援"standlone" applications,可以動態的load並且執行。這些application可以存取U-Boot的I/O,memort和interrupt services。

在U-Boot目錄裡面有example,裡面就有"standlone" application的範例,只要確定U-Boot/Makefile的SUBDIRS有將example包含進去即可。接著我們就用loads指令將hello_world.srec載到U-Boot上,並且執行。注意:要將modem設為ASCII。

這樣可以快速的在U-Boot上發展一些應用程式。



熱門文章