2015/4/27

【Ubuntu】架設PXE Server

PXE開機運作原理是Bootloader(PC來說就是BIOS)在開機時動態要求分配IP,在取得IP資訊同時也會收到下一個伺服器的位址與該讀取的檔案資訊,接著透過TFTP服務取得開機核心與檔案系統。所以一台伺服器要提供PXE功能,至少要有DHCP與TFTP伺服器功能,首先安裝這兩個套件:
sudo apt-get install isc-dhcp-server
apt-get install tftpd-hpa inetutils-inetd

指定提供DHCP服務的網路卡
sudo vi /etc/default/isc-dhcp-server
將interface指定為eth0
INTERFACES="eth0"

設定DHCP服務
sudo vi /etc/dhcp/dhcpd.conf

# option definitions common to all supported networks...
#option domain-name "example.org";
#option domain-name-servers ns1.example.org, ns2.example.org;

subnet 192.168.0.0 netmask 255.255.255.0 {
  range 192.168.0.100 192.168.0.253;
  option routers 192.168.0.254;
  option broadcast-address 192.168.0.255;
}
allow booting;
allow bootp;
option option-128 code 128 = string;
option option-129 code 129 = text;
next-server 192.168.0.254;
filename "pxelinux.0";

default-lease-time 600;
max-lease-time 7200;
若有domain name資訊則填入正確資訊,否則將相關兩的兩行敘述註解起來,否則啟動DHCP服務可能會帶不起來。這邊設定IP分配會從192.168.0.100到192.168.0.253,並指定client端取得IP資訊後接著要向192.168.0.254伺服器要求檔案pxelinux.0。
完成設定後重新啟動DHCP服務
sudo service isc-dhcp-server restart

修改tftp設定
sudo vi /etc/default/tftpd-hpa
增加敘述:
RUN_DAEMON="yes"
修改inet.conf
sudo vi /etc/inetd.conf
增加敘述
tftp dgram udp wait root /usr/sbin/in.tftpd /usr/sbin/in.tftpd -s /var/lib/tftpboot

重新啟動tftp服務
sudo service tftpd-hpa restart

到這邊PXE服務就算設定完成了,這時在client端選擇網路開機,應該會成功取得IP配置,但會卡在取得檔案階段,底下說明使用ubuntu server的安裝光碟裡面提供的netboot檔案,讓client可以透過PXE服務開啟安裝程式。

掛載安裝印象檔,並將需要的檔案複製到tftp目錄即可
sudo mount ubuntu-14.10-server-amd64.iso /mnt
sudo cp -avr /mnt/install/netboot/* /var/lib/tftpboot/

這時client在選擇網路開機,就可以進到ubuntu安裝畫面了!

2015/4/23

【樹莓派】寫Driver控制GPIO

  控制RPi的GPIO方法很多,在User Space下可以透過讀寫/dev/mem或是/sys/class/gpio來控制輸出與讀取狀態;在Kernel Space下就要自己寫一個Driver(或稱為核心模組)來實現了!另外有高手寫了WiringPi讓大家可以簡單透過python來實現IO控制(包括I2C與SPI),也提供API讓C語言之類的程式進行調用。
  這篇文章提供一個簡單的範例,示範如何寫一個驅動程式控制RPi的GPIO。
  首先查看電路圖(Schematic),挑選GPIO_GEN0來連接LED燈正極,並將LED負極接到旁邊的地(Ground,Pin9),如此一來把Pin11設為1就會點亮LED燈,設為0就會讓LED熄滅。

  上圖的GPIO_GEN0是Pin11,但這個Pin11指的是P1這個連接頭(Header)的編號,實際上我們要控制的是BCM2835(RPi的ARM核心)接腳,所以得在電路圖上找尋GPIO_GEN0在核心上對應的GPIO編號,查看下圖得知是GPIO17
確定好要控制的GPIO編號後,就可以來撰寫驅動程式了,直接看Code
rpi.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define DRIVER_AUTHOR    "YS Wang <yaushung AT gmail.com>"
#define DRIVER_DESC    "RPi GPIO Output Demo"
#define GPIO0_PIN    17
#define GPIO0_DESC    "GPIO0"
#define GPIO0_DEVICE_DESC   "GPIO17Output"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

int rpi_init(void) {
 printk(KERN_NOTICE "Hello, RPi !\n");
 
 if (gpio_request(GPIO0_PIN, GPIO0_DESC)) {
  printk("[%s] Request fail\n", GPIO0_DESC);
  return -EIO;
 }

 printk(KERN_ALERT "[%s] Request Pin%d ... Success.\n",
  GPIO0_DESC, GPIO0_PIN);

 gpio_direction_output(GPIO0_PIN, 1);

 return 0;
}

void rpi_exit(void) {
 printk(KERN_ALERT "Goodbye, RPi\n");
 gpio_direction_output(GPIO0_PIN, 0);
 gpio_free(GPIO0_PIN);
 return;
}

module_init(rpi_init);
module_exit(rpi_exit);
Makefile
# make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

CFILES  = rpi.c

obj-m += rpi.o
sample-objs := $(CFILES:.c=.o)

all:
 make -C /home/acos/rpi/linux M=$(PWD) modules

clean:
 make -C /home/acos/rpi/linux M=$(PWD) clean

直接下make(make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-)指令就會產生rpi.ko模組檔案,接著把檔案複製到RPi上執行
sudo insmod rpi.ko      # 模組載入,這時會看到LED點亮
sudo rmmod rpi      # 模組卸除,這時會看到LED熄滅

編譯環境設置可以參考這篇文章

2015/4/20

【樹莓派】架設DHCP Server

首先安裝DHCP Server套件
sudo apt-get install isc-dhcp-server
安裝完畢系統會嘗試帶起服務,但設定都還沒弄好所以會看到錯誤訊息,忽略即可。
[FAIL] Starting ISC DHCP server: dhcpd[....] check syslog for diagnostics. ... failed!
 failed!
invoke-rc.d: initscript isc-dhcp-server, action "start" failed.

設定eth0網卡IP為192.168.0.1,修改/etc/network/interfaces文件
#iface eth0 inet dhcp
allow-hotplug eth0
iface eth0 inet static
  address 192.168.0.1
  netmask 255.255.255.0
  gateway 192.168.0.1

設定dhcp組態文件/etc/dhcp/dhcpd.conf,文件中有很多範例可以參考,依據自己需求挑一個合適的範本進行設定,這裡讓Server會配發192.168.0.200到192.168.0.253之間的IP給Client端
subnet 192.168.0.0 netmask 255.255.255.0 {
  range 192.168.0.200 192.168.0.253;
  option routers 192.168.0.1;
  option broadcast-address 192.168.0.255;
  default-lease-time 600;
  max-lease-time 7200;
  option domain-name-servers 168.95.1.1;
}

編輯/etc/default/isc-dhcp-server文件,指定提供DHCP服務的網卡,找到INTERFACES敘述,填上eth0
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#       Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACES="eth0"

最後重新啟動服務,看到ok就表示服務正常啟動了
sudo service isc-dhcp-server restart
[ ok ] Stopping ISC DHCP server: dhcpd.
[ ok ] Starting ISC DHCP server: dhcpd.

這時接上一台電腦應該就會自動獲取RPi指派的IP了,可以搭配【無線網路轉送有線網路橋接】設定封包轉送服務,這樣RPi就可以當作一台簡易的無線轉有線橋接器!

2015/4/18

【樹莓派】無線網路轉送有線網路橋接

參考下圖,需求是讓RPi連接無線的網路訊號並用網路線連接到一台電腦,讓電腦能夠透過樹莓派來上網。
無線網路的設定可以參考【設定無線網路】,這篇文章著重在有線的網路封包轉傳。在Linux上要實現封包轉送只需要設定好iptable就可以了!

首先編/etc/sysctl.conf檔案,將net.ipv4.ip_forward=1這行註解拿掉
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
重新載入設定檔,讓設定生效
sysctl --system

完成上述動作RPi的iptable功能就被啟動了,接著設定RPi的eth0網卡IP設定為192.168.0.1
,修改/etc/network/interfaces
#iface eth0 inet dhcp
allow-hotplug eth0
iface eth0 inet static
  address 192.168.0.1
  network 192.168.0.0
  netmask 255.255.255.0
  broadcast 192.168.0.255
  gateway 192.168.0.1
修改後重新啟動eth0
ifconfig eth0 down
ifconfig eth0 up

最後設定iptable,在家目錄下編輯一個新檔nat.sh
#!/bin/sh
IPT=/sbin/iptables
LOCAL_IFACE=eth0
INET_IFACE=wlan0
INET_ADDRESS=`ifconfig wlan0 | grep "inet addr" | cut -d ' ' -f 12 | cut -d : -f 2`

# Flush the tables
$IPT -F INPUT
$IPT -F OUTPUT
$IPT -F FORWARD

$IPT -t nat -P PREROUTING ACCEPT
$IPT -t nat -P POSTROUTING ACCEPT
$IPT -t nat -P OUTPUT ACCEPT

# Allow forwarding packets:
$IPT -A FORWARD -p ALL -i $LOCAL_IFACE -j ACCEPT
$IPT -A FORWARD -i $INET_IFACE -m state --state ESTABLISHED,RELATED -j ACCEPT

# Packet masquerading
$IPT -t nat -A POSTROUTING -o $INET_IFACE -j SNAT --to-source $INET_ADDRESS

執行nat.sh
chmod +x ~/nat.sh
~/nat.sh

到這裡一切就設定完成了,接著在電腦端設定固定IP(ex. 192.168.0.2)、路由與DNS等資訊就可以透過RPi上網了。

【樹莓派】編譯一個Hello Kernel Module(Driver)

  編譯Driver給RPi使用需要準備Toolchain與Kernel Source Tree,步驟可以參考【重編Kernel,客制化自己的核心】,以下範例目錄結構為
 /home/acos
--+ rpi
----+ linux: 存放Kernel Source
----+ tools: 存放Toolchain
----+ driver: 存放此範例檔案hello.c與Makefile

hello.c
#include <linux/module.h>
#include <linux/init.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
 printk(KERN_ALERT "Hello, RPi!\n");

 return 0;
}

static void hello_exit(void)
{
 printk(KERN_ALERT "Goodbye, RPi\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile
obj-m := hello.o

all:
 make -C /home/acos/rpi/linux M=$(PWD) modules

clean:
 make -C /home/acos/rpi/linux M=$(PWD) clean

進到driver目錄下執行
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
結束後會在目錄中看到hello.ko,將這檔案複製到RPi上進行載入與卸除動作就會看到對應訊息
root@raspberrypi:~# insmod hello.ko
[ 5835.187137] Hello, RPi!
root@raspberrypi:~# rmmod hello
[ 5842.134762] Goodbye, RPi

2015/4/17

【樹莓派】開機自動登入

要讓RPi自動登入並執行預設程式很簡單,只需要修改/etc/inittab檔案與登入帳號家目錄下的.profile檔案即可。

首先設定自動登入,編輯/etc/inittab檔案:
sudo vi /etc/inittab
把最後一行敘述T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100註解起來,並加入T0:23:respawn:/bin/login -f pi ttyAMA0 </dev/ttyAMA0 >/dev/ttyAMA0 2>&1
#Spawn a getty on Raspberry Pi serial line
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
T0:23:respawn:/bin/login -f pi ttyAMA0 </dev/ttyAMA0 >/dev/ttyAMA0 2>&1
存檔重開機,就會自動使用pi帳號完成登入,若想要以root身分自動登入,則將上面敘述的pi改為root即可。

自動登入後若想自動執行程式,可以從~/.profile帶起。

2015/4/15

【樹莓派】重編Kernel,客制化自己的核心

編譯Linux Kernel,準備好Toolchain(即Compiler, Linker等工具)與Kernel Source就可以開始了!此範例採用Cross-Compile,也就是在一般X86架構的PC上為樹莓派編譯核心,編譯完成在將相關檔案複製到SD Card上讓樹莓派使用。

過程中會使用到git抓取toolchain與kernel source,因此須先安裝git套件:
sudo apt-get install git-core

接著在家目錄建立rpi目錄並使用git下載toolchain與kernel source
cd ~
mkdir rpi
cd rpi
git clone https://github.com/raspberrypi/tools
git clone --depth=1 https://github.com/raspberrypi/linux
完成上述動作後可在rpi目錄中看到tools與linux兩個子目錄

為了方便後續操作,將tools套件所在目錄加到$PATH變數
vi ~/.bashrc
在檔案尾端加入敘述
PATH=$PATH:/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin
如果使用的作業系統平台是64位元的話,需換成下面這敘述
PATH=$PATH:/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
修改完成後重新關聯~/.bashrc讓設定生效
source ~/.bashrc

完成工具與環境設定後,就可以開始進行核心編譯了
進入kernel source目錄,設定預設值,進行編譯!
cd ~/rpi/linux
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcmrpi_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
如果使用的是多核心CPU可以在make指令中使用-j <n>參數讓編譯動作分散在各核心中加快整體速度,其中<n>是核心數量,過一段時間完成後若沒出現錯誤就已經完成核心編譯了。

最後將SD卡插入電腦,把剛編譯好的模組檔案與核心檔案取代原先檔案後即完成所有步驟
mkdir /mnt/fat32
mkdir /mnt/ext4
sudo mount /dev/sdb1 /mnt/fat32
sudo mount /dev/sdb2 /mnt/ext4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/mnt/ext4 modules
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=/mnt/ext4 modules_install
sudo cp /mnt/fat32/kernel.img /mnt/fat32/kernel-backup.img
sudo cp arch/arm/boot/Image /mnt/fat32/kernel.img
sudo umount /mnt/fat32
sudo umount /mnt/ext4

將SD卡插入樹莓派,開機登入後查看歡迎訊息顯示的核心資訊,可以驗證一下核心產生的時間是不是正確的!

2015/4/9

【樹莓派】查看版本、規格與製造商

在第一代不同的Model在GPIO腳位的設計上有些許的不同,板子上的記憶體大小也不一樣,要知道自己手上的RPi是屬於哪個版本、製造商是誰可以透過查看CPU資訊來得知
cat /proc/cpuinfo
顯示結果會有像下面的訊息
Hardware        : BCM2708
Revision        : 000e
Serial          : 00000000bf257857
依據Revision再去查表即可知道詳細的相關資訊
像手上這塊板子是000e,則可以知道是Model B 2.0、記憶體是512MB,由Sony製造。
表格資訊會隨時間持續增加項目,最新資料可到elinux.org查詢。