2016年1月18日 星期一

linux driver 入門 Hello world

來動手寫一個Hello world 的字元驅動程式吧

================================================
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
int hello_init(void)
{
        printk("hello world!\n");
        return(0);
}

void hello_exit(void)
{
        printk("goodbye world!\n");
}

MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
=================================================

當 make完後....產生 ko file

insmod hello.ko

我們可以看看有甚麼變化....他會在  /proc/modules 發生變化

 輸入 cat /proc/modules

  就可以看到下面的訊息....



  模組名   使用的記憶體     參考計數   分隔符號      活耀狀態    載入在核心的位置
   hello       733                       0                       -                  Live         0x7f23_4000

然後在  /sys/module/目錄下面會增加一個hello 模組的基本資訊

輸入    tree -a /sys/module/hello

可以看到下面這些資訊




接下來繼續增加檔案....
==============#include<linux/module.h>
#include<linux/fs.h>
#include<linux/kernel.h>
#include<linux/init.h>

#define HELLO_MAJOR 234
static struct file_operations hello_fops ;


int hello_init(void)
{
       int ret;
        printk("hello world!\n");

       ret = register_chrdev(HELLO_MAJOR, "hello1", &hello_fops);

       if(ret <0){
        printk("Error registering hello device\n");
        return ret;}

         /*   Init processing here ... */

        return(0);
}
void hello_exit(void)
{
        printk("goodbye world!\n");
}

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("HELLO WORLD Example");
module_init(hello_init);
module_exit(hello_exit);

static int hello_open(struct inode * inode, struct file *file)
{
      printk("hello_open : successful\n");
      return 0;
}

static int hello_release(struct inode * inode, struct file *file)
{
      printk("hello_release : successful\n");
      return 0;
}


static int hello_read(struct file *file, char *buf, size_t count, loff_t *ptr)
{
      printk("hello_read : returning zero bytes\n");
      return 0;
}

static int hello_write(struct file *file,const char *buf, size_t count, loff_t *ptr)
{
      printk("hello_write : accepting zero bytes\n");
      return 0;
}
/*
static int hello_ioctl(struct node *inode,struct file *file, unsigned int cmd, unsigned long arg)
{
      printk("hello_ioctl : cmd=%ld, arg=%ld\n",cmd,arg);
      return 0;
}
*/

static struct file_operations hello_fops = {
     owner : THIS_MODULE,
     read  : hello_read,
     write : hello_write,
  //   ioctl : hello_ioctl,
     open  : hello_open,
     release: hello_release
};

========================================================

當進到 hello_init function時.....

我們註冊了 字元裝置

ret = register_chrdev(HELLO_MAJOR, "hello1", &hello_fops);

模組名是 hello1

然後我們把放著函式指標的結構傳進去     &hello_fops

這個函式指標會把相對應的function 連接起來....

像是在 user space  時..... 如果呼叫   open function 就會連接到  hello_open function....

寫完之後....make.....然後   sudo insmod hello.ko

然後建立一個節點

mknod /dev/hello1   c 234 0

c 代表  字元驅動程式

234 為  major number...... 0 為 sub-major number

到目前為止....已經把hello driver 掛起來變成一個檔案....

接下來就是寫一個user space 的範例...來呼叫hello driver

新增一個 main.c file..然後把下面的內容加下去
==========================================


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

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

int fd;
int rc =0;

char* rd_buf[16];

printf("%s : enterd\n",argv[0]);

/*  open device */
fd = open("/dev/hello1",O_RDWR);

if(fd ==-1){
   perror("open failed");
   rc = fd;
   exit(-1);
}

printf("%s : open : successful\n",argv[0]);


/* issue a read */
rc = read(fd,rd_buf,0);
if( rc == -1){
     perror("read failed");
     close(fd);
     exit(-1);
}

printf("%s: read : returning %d bytes!\n", argv[0],rc);

close(fd);

return 0;
}

==========================================

輸入   gcc  main.c

預設的輸出黨名為  a.out   

sudo ./a.out

然後輸入   dmesg

就可以看到  driver 打印出來的訊息



這是因為 main.c 當中

呼叫了  open  ->  hello_open -> "hello_open : successful"
呼叫了  read  ->  hello_read -> "hello_read : returning zero bytes"
呼叫了  close  ->  hello_release -> "hello_release : successful"


Reference : Linux 驅動程式開發權威指南 第二版 第五章
Reference : Embedded linux 嵌入式系統開發實務 第八章

編譯核心時...使用 make menuconfig 來打造你的核心

為什麼要編譯核心?
新功能的需求、原本核心太過臃腫、與硬體搭配的穩定性、其他需求(如嵌入式系統)。
上面的話是擷取自參考2來的......


在下載完 linux kernel code後...在folder 下面輸入

make menuconfig

就可以指定你的option


Reference 1 : http://linux.vbird.org/linux_basic/redhat6.1/linux_10kernel.php

Reference 2 : https://www.raspberrypi.com.tw/528/compiling-kernel-for-raspberry-pi/