經由前篇的文章介紹,相信也對整個流程和相關 struct有所了解,讓我在PO另外一個用法,keyword "proc_dir_entry",Linux的PROC檔案系統是procedure檔案系統和kernel檔案系統的組成的,是kernel模擬出來的軟體檔案系統,讓kernel對user space的資訊窗口,所以你可以看到/proc的每一個檔案都代表kernel裡面專屬的function,linux的工具像是ps、top也是從這邊提取所要的資訊,雖然它是一個很便捷的寫法,在debug上也有相當高的便利,但取決哪種用法還是像我一開始說的那樣,在面對不同的device要用不同的方式,不然就枉費linux提供相當多可以對應的struct and method,不過在i2c來說算是適合用這種寫法才對。

再來我們一樣來看要用的struct,這次應該有注意到它提供的讓你填入function並沒有那麼多,相對提供了很多parameter,但主要我用到的還是read/write,如果你在去看read_porc_t的struct,會有一個很重要的東西就是page,page是來自kernel輸入的parameter,其餘的都是輸出的參數,若是有太多資料是無法達成一次傳輸,要用offset來分批傳輸,下面code有寫出來。

struct proc_dir_entry {
    unsigned short low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;
    unsigned long size;
    struct inode_operations * proc_iops;
    struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;

    atomic_t count;        /* use count */
    int deleted;        /* delete flag */
    kdev_t    rdev;
};

請注意到你在ap使用read data的時候,他這邊可以直接使用page回傳,而不用另外call function ,但是要注意的就是length 應該是互相對應的,不然很容易read最後會出現很奇怪的data。


static int read_slot(char *page, char **start, off_t off,
                int count, int *eof, void *data)
{
    int len,i;

    len = sprintf(page, "Slot table \n");
    for(i=0;i<SLOT_NUM;i++){
        if(slot_table[i].slot_detect){
            OP_LOCK();
            len += sprintf(page+len,".........." ,.............);
            OP_UNLOCK();
        }else{
            len += sprintf(page+len, "............", (i+1));
        }
    }

    if (len <= off+count) *eof = 1;
    *start = page + off;
    len -= off;
    if (len > count) len = count;
    if (len < 0) len = 0;
    return len;

}
而在write這邊你也看到,他這裡是用copy_from_user,將ap資料抓到kernel。
static int write_slot(struct file *file, const char *buffer, unsigned long count, void *data)
{
    int i;
    char cmd;
    unsigned char argv[32];
    unsigned char slot;
    unsigned char tp_conf,fx_conf;
    char command[32];

    if (count < 2) return -EFAULT;

    cmd = 0;
    memset(command,0,32);
    memset(argv,0,32);

    if (buffer && !copy_from_user(&command, buffer, count)) {
        OP_LOCK();
        if(count >0){
            .....................................
            }
            OP_UNLOCK();
        }else{
            DPRINTK("Command Length Error (%d):\r\n",count);
        }
        return count;
    }else
        return -EFAULT;
}

static void gpio_timer(unsigned long data)
{
       ..........................................

ret_timer:
    mod_timer(&probe_timer, jiffies + 25);
}

一樣code寫的很多,我也亂砍掉一些講解所需部份,這時候應該要有警覺到他的註冊function "create_proc_entry",create_proc_entry只針對proc可以提供註冊,這又跟我之前不大一樣了,若是你只想寫成only read,也可以用create_proc_read_entry,這時候你會發現follow整個linux driver所提供的架構下去寫很難,為什麼?就是因為它有太多現成的function可以對應,所以之前提到為什麼要拿現成的來撰寫,對我而言初學者如何進步最快,就是摹仿人家的寫法,在針對你的需求去學習,再拓展成去鑽研細節的部份。

int __init rtl_gpio_init(void)
{

    int slotno;

    struct proc_dir_entry *res=NULL;


    // Set GPIOA reset button
    GPIO_IN(GPIO_A,RESET_BTN_PIN);

    //Console LED Off
    GPIO_OUT(GPIO_D,CONSOLE_LED_PIN);
    GPIO_LO(GPIO_D,CONSOLE_LED_PIN);

    //Enable system LED
    GPIO_OUT(GPIO_A,SYS_LED_PIN);
    GPIO_LO(GPIO_A,SYS_LED_PIN);
    SYS_LED_MODE = SYS_LED_BLINK_ON;

    //Enable slot detect
    GPIO_OUT(GPIO_A,SLOT_DETECT_PIN);
    GPIO_HI(GPIO_A,SLOT_DETECT_PIN);
    GPIO_IN(GPIO_A,SLOT_DETECT_PIN);

    init_slot();        // Slot init
    init_redundant();    // Redu init

    init_.........._table();

#ifdef CONVERTER
    res = create_proc_entry("slot", 0, NULL);
    if (res) {
        res->read_proc = read_slot;
        res->write_proc = write_slot;
    } else {
        printk("GPIO Driver, create proc/slot failed!\n");
    }
 .................................................................
#endif

/*

再這裡要提到一個好東西就是timer,當你的device沒有提供interrupt,而你想針對週邊event來處理,就可以用timer代為處理這類event(當然irq也是一種選擇),但建議是越精簡越好,他是會吃資源的東西,當你在這邊判斷大量的資訊,會很明顯的拖垮速度,用法很簡單先看到他的expires,這是當計數到多少的才call function,function當然就是你想執行的function,data是一個回傳value除非啦你是用外部function不然沒必要特地填入東西state也是,使用方式就是這麼簡單,這個是不管你在使用什麼方式下去寫driver,都可以呼叫來使用的api。

*/

   init_timer (&probe_timer);
    probe_counter = 0;
    probe_state = PROBE_NULL;
    probe_timer.expires = jiffies + 100;
    probe_timer.data = (unsigned long)NULL;
    probe_timer.function = &gpio_timer;
    mod_timer(&probe_timer, jiffies + 100);

    return 0;
}


static void __exit rtl_gpio_exit(void)
{
    printk("Unload i2c GPIO Driver \n");
    del_timer_sync(&probe_timer);
}

module_exit(gpio_exit);
module_init(gpio_init);

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

ap端的寫法

int main(int argc, char *argv[])
 {            

   FILE *fp;

    unsigned char strbuf[100];                   

    memset(strbuf,0,sizeof(strbuf));

    sprintf(strbuf,"echo s%02dv%s > /proc/slot",slot_num,slot_status);

                      system(strbuf);          

     fp = fopen("proc/slot", "r");
        if (!fp) {
            printf("Read slot failed!\n");
            fclose(fp);
        }
        else
        {
            memset(strbuf,0,sizeof(strbuf));
            printf("\r\n\r\n");

            sprintf(strbuf,"cat proc/slot | grep \"Slot 1 \" | awk '{print $4}' > /tmp/temp");

            fclose(fp);

        }  

}

在來要補充的是copy_from_user and copy_to_user 在上篇有用char device driver(字元驅動)方式和這篇proc的方式,這兩個方法要用什麼其實是見仁見智,但共通從ap(user space) transport data to kernel space,這個是一種方式並非唯一的方式,linux提供這個方式來降低你從user space使用對應device memnory,有可能發生的錯誤,這function就是提供機制不讓你錯誤使用時候,直接對系統造成太大影響類似當機之類的,也提供一個interface 去溝通,當error return value告訴你,而"可能"不會直接對內部有影響。

arrow
arrow
    全站熱搜

    funlife1 發表在 痞客邦 留言(1) 人氣()