2013年10月13日 星期日

Week3

module1:
This restaurant is famous for its seafood and well-serviced quality.Customers have to make a reservation before dining.There are many kinds of seafood on the menu,such as fried-shrimps,roasted-lobster, and steamed-fish.Everyone enjoys their meals there. Due to the fresh seafood and its low price, this restaurant gains high popularity in this small town.

module2: (underline action verbs)
This restaurant is famous for its seafood and well-serviced quality.Customers have to make a reservation before dining.There are many kinds of seafood on the menu,such as fried-shrimps,roasted-lobster, and steamed-fish.The main chef strongly recommends the  roasted-lobster with butter  for main course. The lobster with fragrant butter tastes delicious that everyone enjoys it there. Due to the fresh seafood and its low price, this restaurant gains high popularity in this small town.

Assignment (improvement) :
This restaurant is famous for its seafood and well-serviced quality. There always has a long line of people outside the door, waiting for tasting seafood expectantly. Customers have better to make a reservation before dining. There are many kinds of seafood on the menu, such as fried-shrimps, roasted-lobster, and steamed-fish. The main chef strongly recommends the roasted-lobster with butter for main course due to its freshness. The lobster with fragrant butter tastes delicious that everyone enjoys it there. Due to the fresh seafood and its low price, this restaurant gains higher and higher popularity in this small town. Therefore, the local tourism also gets stronger and brings economic recovery of the town.

2013年9月1日 星期日

Linux Device Driver - IOCTL (copy_to_user & copy_from_user)

在 linux/include/linux/fs.h 中定義許多file operation能用的handler
這一次我針對的是 read、ioctl做練習
file operation中沒實做到的handler會被定義成NULL
這一支程式會先得到來自kernel的回應,接著kernel 換更改指定的數字

先大概說一下主要概念
我要先寫一個mydev.c 當作 driver
執行完make 之後 就把 mydev.ko 加入到 kernel中
此時的 mydev.ko 就是模組, 一個在kernel中的模組了
那要如何與此模組互動呢?
我要再寫一個 test_mydev.c 當作 user
直接把他 gcc 產生執行檔 再指定我要跟mydev 模組溝通

首先先來看 ioc.h
這裡定義了 user 能夠透過 ioctl 傳達的指令
IOC_MAGIC 是device 的 major number , 而我定義其major number為 k
每一個裝置都有其major number 與 minor number
接著我設置 SETNUM  GETNUM  XNUM 3種指令
_IOW  _IOR  _IOWR 是 kernel 的巨集 , 我們用這3個巨集來定義我們的命令要做哪些事
各聚集裡的1,2,3 是自訂的minor number

#ifndef IOC_H
#define IOC_H

#define IOC_MAGIC 'k'
#define SETNUM _IOW(IOC_MAGIC, 1 , int)
#define GETNUM _IOR(IOC_MAGIC, 2 , int)
#define XNUM _IOWR(IOC_MAGIC,3 , int)
#define IOC_MAXNR 3

#endif


接著看到最主要的 driver code : mydev.c
在file operations 中定義了我自訂的操作函數
dev_open : 在user open  device file 時會被呼叫
dev_close : 在user close device file 時會被呼叫
dev_ioctl  :  switch case 中寫了各種command該執行的動作
         若user 呼叫 ioctl 時傳入 SETNUM 則 driver 會將傳入的資料(args)取出,並指定給val 變數
         若user 呼叫 ioctl 時傳入 GETNUM 則 driver 會將val 變數傳出(args)
         若user 呼叫 ioctl 時傳入XNUM 則 driver 會將 user 輸入的數值指派給val變數達到更改資料
         的目的
         (因為user space 與 kernel space 儲存data的空間不同, 因此要用 copy_to_user 與
         copy_from_user來轉遞資料)
dev_read : user 呼叫 read時會被呼叫, 我在這裡放了一個hello字串, kernel會利用copy_to_user 傳到
                 buffer中給user
dev_write: 我在這裡沒用到
init_modules:  程式進入點 , 建立裝置並註冊到kernel
exit_modules: 程式結束 , 移除裝置

#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()

#include "ioc.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IRENE 2013/09/01");
#define DEV_BUFSIZE 1024

static int dev_major;
static int dev_minor;
struct cdev *dev_cdevp = NULL;
static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
static int dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
ssize_t dev_read(struct file*, char __user*, size_t, loff_t*);
ssize_t dev_write(struct file*, char __user*, size_t, loff_t*);
static void __exit exit_modules(void);

//Functions on the right side are the handler we just defined
struct file_operations dev_fops = {
   .owner = THIS_MODULE,
   .open = dev_open,
   .release= dev_release,
   .ioctl = dev_ioctl,
   .read = dev_read,
   .write = dev_write
};


// dev_open will be Called when user open the device file
static int dev_open(struct inode *inode, struct file *filp)
{
       printk("%s():\n",__FUNCTION__);
       return 0;
}

// dev_release will be Called when user close the device file
static int dev_release(struct inode *inode, struct file *filp)
{
       printk("%s():\n",__FUNCTION__);
       return 0;
}

// mainly io control between user and kernel
// args are pointed to user space's buffer
static int val = 0;
static int dev_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long args)
{
      int tmp, err=0,ret=0;

      if(_IOC_TYPE(cmd) != IOC_MAGIC)
              return -ENOTTY;
      if(_IOC_NR(cmd) > IOC_MAXNR)
              return -ENOTTY;
      if(_IOC_DIR(cmd) & _IOC_READ){
              err = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd));
      } else if (_IOC_DIR(cmd) & (_IOC_WRITE)){
              err = !access_ok(VERIFY_READ, (void __user *)args,_IOC_SIZE(cmd));
      }

      if (err)
              return -EFAULT;

      switch (cmd) {
        case SETNUM:
             //copy data from args(user) to val(kernel)
             if(copy_from_user(&val,(int __user *)args,1))
                 return -1;

             printk("%s():get val from user = %d\n",__FUNCTION__,val);
                   break;
        case GETNUM:
                        //copy data from val to args
              if(copy_to_user((int __user *)args,&val,1))
                    return -1;

              printk("%s():set val to %d\n",__FUNCTION__, val);
                    break;
        case XNUM:
                       // exchange data passed by user
              tmp = val;
              if(copy_from_user(&val,(int __user *)args,1))
                        return -1;

              if(copy_to_user((int __user *)args,&val,1))
                        return -1;

           printk("%s():change val from %d to %d\n",__FUNCTION__,tmp,val);
           break;

        default: /* redundant. as cmd was checked against MAXNR */
                 return -ENOTTY;
     }
     return 0;
}


static const char str[]="Hello Irene from kernel!";
static const ssize_t str_size = sizeof(str);
// send messages to user
ssize_t dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{

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

     if( *f_pos >= str_size)
          return 0;
     if( *f_pos + count > str_size)
        count = str_size - *f_pos;
     if (copy_to_user(buf, str + *f_pos, count))
        return -EFAULT;
     *f_pos += count;
     return count;

}

ssize_t dev_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
     printk("%s():\n",__FUNCTION__);
     return 0;
}

static int __init init_modules(void)

   dev_t dev;
   int ret;
   // get major number dynamically
   ret = alloc_chrdev_region(&dev, 0, 1, "mydev");
   if (ret <0){
      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 = kmalloc(sizeof(struct cdev), GFP_KERNEL);
   if (dev_cdevp == NULL){
        printk("kmalloc failed\n");
        goto failed;
   }
   // system call handler
   cdev_init(dev_cdevp, &dev_fops);
   dev_cdevp->owner = THIS_MODULE;
   //register my device to kernel
   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);


接著 是 user 端部分啦 test_mydev.c
執行時要輸入的參數有 devicefile num
device file 就是 在 /dev下的裝置檔名
num 是 XNUM命令時要change的value (default 是 10 )
首先利用 open 打開 device file
然後 read(fd, buffer, sizeof(buffer));
buffer 是 user 拿來放kernel 回傳值的地方
此時kernel 會回傳 Hello Irene from kernel!
接著 user 開始下達 ioctl command
--> ioctl(fd, SETNUM, &ret)
這是其中一個例子 , SETNUM 便是user 下達的command
而&ret 是 回傳值的指標

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ioc.h"

int main(int argc, char *argv[])
{
       int fd, ret=10;
       unsigned char buffer[50]={};
       if(argc < 2)
       {
         printf("Usage: /dev/mydev param1.. \n");
         return -1;
       }
       // open dev file
       fd=open(argv[1], O_RDWR);
       if(fd < 0)
       {
           printf("open %s failed\n", argv[1]);
           return -1;
       }
       //read from kernel 
       read(fd, buffer, sizeof(buffer));
       printf("%s\n",buffer);
       // set num to 10
       if (ioctl(fd, SETNUM, &ret)< 0)
       {
           printf("set num failed\n");
           return -1;
       }
       if(ioctl(fd, GETNUM , &ret)< 0)
       {
           printf("get num failed\n");
       }
       printf("get default value=%d\n",ret);
       // exchange number ret =atoi(argv[2]); 
       if (ioctl(fd, XNUM, &ret)<0)
       {
           printf("exchange num failed\n");
           return -1;
        }
       printf("get value = %d\n",ret);
       return 0;
}


最後是 Makefile








上面全部的檔案都存在mydev 資料夾中
/usr/src/kernels/linux-2.6.18/mydev/..

開始執行
make 之後的指令如下












在 insmod之後 dev會被賦予 major number 與 minor number 
開啟新的cmd 執行dmesg就可以看到kernel傳來一條msg
register  chrdev(252,0)
或是可以到 /proc/devices中查看 major number 與 minor number
在mknod時一定要給定對的major 與 minor number
否則會有 找不到 file 的錯誤
執行後可以看到
kernel 傳回一條 hello messages
而kernel也將預設為10 的value改為 user 設定的 100
最後dmesg中可以看到kernel的訊息












研究好多天仍有很多疑問, 有不完備之處之後繼續修改
參考:
 http://csw17.pixnet.net/blog/post/11506143
http://nano-chicken.blogspot.tw/2009/12/linux-modulesv-ioctl.html
http://www.codeproject.com/Articles/112474/A-Simple-Driver-for-Linux-OS
強烈建議大家在寫這個之前先到jollen的網站看相關的 device 觀念教學

2013年8月25日 星期日

Linux Kernel Module 3 - command line argument

1. 由command line傳入的value會被視為global
2. 使用module_param() macro來加入變數
3. 在runtime時, insmod會填入從command line輸入的value到相應的變數
4. module_param()有3個params,依序為 變數名稱, 變數型態, 權限
    module_param(name, type, permission)
   目前支援的type有 byte, short, ushort, int, uint, long, ulong, charp, bool
5.若要傳入array或string可以使用 module_param_array() or module_param_string()
  array的話在type之後要多一個count , module被載入的時候user所指定的array length會被fill到此變
  數中 , NULL也可以
6. 使用 module_param_desc()則可以對某一變數做描述

/*
 * hello_5.c - Demonstrates command line argument passing to a module.
 */
#include &lt;linux/module.h&gt;
#include &lt;linux/moduleparam.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/stat.h&gt;

MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;Irene&quot;);

static short int myshort = 1;
static int myint = 420;
static long int mylong = 9999;
static char *mystring =&quot;blah&quot;;
static int myintArray[2]= {6,6};
static int arr_argc = 0;

/*
 * module_param(foo, int , 0000)
 * The first param is the parameters name
 * The second param is it&#39;s data type
 * The final argument is the permissions bits,
 * for exposing parameters in sysfs (if non-zero) at a later stage.
 */

module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myshort, &quot;A short integer&quot;);
module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP |S_IROTH);
MODULE_PARM_DESC(myint, &quot;An integer&quot;);
module_param(mylong, long, S_IRUSR);
MODULE_PARM_DESC(mylong, &quot;A long integer&quot;);
module_param(mystring, charp, 0000);
MODULE_PARM_DESC(mystring, &quot;A character string&quot;);

/*
 * module_param_array(name, type, num, perm);
 * The first param is the parameter&#39;s (in this case the array&#39;s)name
 * The second param is the data type of the elements of the array
 * The third argument is a pointer to the variable that will store the number
 * of the elements of the array initialized by the user at module loading time
 * The fourth argument is the permission bits
 */
module_param_array(myintArray, int, &amp;arr_argc, 0000);
MODULE_PARM_DESC(myintArray, &quot;An array of integers&quot;);

static int __init hello_5_init(void)
{
int i;
printk(KERN_INFO &quot;Hello, world 5\n==============\n&quot;);
printk(KERN_INFO &quot;myshort is a short integer: %hd\n&quot;, myshort);
printk(KERN_INFO &quot;myint is an integer: %d\n&quot;, myint);
printk(KERN_INFO &quot;mylong is a long integer: %ld\n&quot;, mylong);
printk(KERN_INFO &quot;mystring is a string: %s\n&quot;,mystring);
for(i=0; i&lt;(sizeof myintArray / sizeof(int));i++)
{
printk(KERN_INFO &quot;myintArray[%d] = %d\n&quot;, i,myintArray[i]);
}
printk(KERN_INFO &quot;got %d arguments for myintArray.\n&quot;, arr_argc);
return 0;
}
static void __exit hello_5_exit(void)
{
printk(KERN_INFO &quot;Goodbye, world 5\n&quot;);
}

module_init(hello_5_init);
module_exit(hello_5_exit); 


利用 insmod hello_5.ko 看output messages ( 利用 cat /var/log/messages)








或是 insmod hello_5.ko myint=25 mystring="Irene" myintArray=1,2









可以看到指定的變數都改value了!




2013年8月24日 星期六

Linux Kernel Module 2 - 顯示 module info

在 Linux  /include/linux/module.h 中可以找到定義一些module基本info的function
for example:

 MODULE_DESCRIPTION(DESC) : 定義MODULE在做些什麼
 MODULE_AUTHOR(AUTHOR) : 明顯的就是告訴大家作者是誰
 MODULE_LICENSE(GPL) : 宣告GPL

至於上面的DESC 和 AUTHOR 可以用 #define 來預先定義
#define DESC "this is description"
#define AUTHOR "I am author"

這是書中 hello_4.c 範例
寫完一樣 make和 insmode
再來執行 modinfo hello_4.ko 就會顯示info了











2013年8月16日 星期五

Linux Kernel Module 1 - 第一支Hello Irene Module

1. 先查詢自己的Linux version
    --> uname -r
    我的版本是 Linux-2.6.18

2. 下載Linux Kernel Source Code (壓縮檔放在/root底下)
     選擇 linux-2.6.18.tar.bz2

3. 把 source code放在 /usr/src/kernels/ 目錄下
    --> tar -jxvf  linux-2.6.18.tar.bz2 -C /usr/src/kernels/
    此時kernels下會有一個linex-2.6.18資料夾

4.進入kernel 目錄
   --> cd /usr/src/kernels/linux-2.6.18

5. 建立存放module的資料夾
   --> mkdir hello

6.開始寫內部程式
   (1) hello-1.c
      --> vim hello-1.c
      --> i  (編輯模式)
      -->按 Esc 回一般模式
      --> :wq (儲存並離開)
     


      (2) Makefile
        --> vim Makefile
        
       


7 --> make
make 之後會產生 hello-1.ko 


8 --> insmod ./hello-1.ko
將hello-1.ko 加入至kernel module中
可以到 /proc/modules/中看到hello-1 module正在kernel中


此時利用 cat /var/log/messages 到messages中會看到 Hello Irene

9. --> rmmod ./hello-1.ko
將hello-1 module移除
此時 /proc/modules/中就看不見 hello-1 module了
而到 /var/log/messages/ 會看到 Goodbye Irene


P.S 想要搜尋Hello與 Goodbye,可使用grep 指令
--> cat /var/log/messages | grep Hello
--> cat /var/log/messages | grep Goodbye

第一次寫kernel的東西~我連linux指令都還不太熟悉@@
希望繼續加油囉!
-------------------------------------------------------------------
參考 :  鳥哥的 Linux私房菜 第三版  ch22,ch26
           The Linux Module Programming Guide 
            





全國推廣動物認領養平台串聯貼紙

全國推廣動物認領養平台串聯貼紙