2018年2月6日 星期二

Thermal

當能耗增加, 熱也會伴隨增加. 電子產品必須有電流通過才能運作, 所以只要使用時就會產生發熱現象. 工業界有定義一個安全的溫度範圍, 主要目的是為了避免使用者在使用時被燙傷. 根據文獻(ISO 13732-1 or EN 563 or UL/IEC 62368-1)中記載, 不管事塑膠, 陶瓷或是金屬, 只要在溫度48度握著超過10分鐘都會有被燙傷的機會.

根據國際安全標準UL/IEC 62368-1的規定, 手持式裝置的表面溫度僅可達到攝氏48, 倘若過熱就可能會有燙傷,電池損害..等安全疑慮. 換言之,智慧型手機使用時溫度不能超過攝氏48, 否則就是過熱, 有安全問題.




手機過熱的原因 :

手機只要使用, 因為產生功耗, 就會有溫度. 智慧型手機在一般上網或通話使用時手機溫度在攝氏35~40℃微溫狀態是正常的, 但若持續超過40℃以上就算過熱現象, 若超過48℃以上則有安全性的疑慮. 手機為何會過熱? 主要原因如下:

A. 高耗能的APP軟體運作(如玩3D遊戲或播放影片)或大量資料傳輸造成CPU持續以高負載運行
B. 邊充電邊玩遊戲或通話
C. 使用不良的鋰電池
D. 手機通訊軟體故障(拉住wakelock)
E. 訊號不好(-100dB)

由於目前智慧型手機大都具備多工功能, 使用耗能的APP軟體(如遊戲、Flash、相機、看影片或大量資料傳輸), Thermal的部分沒處理好, 手機溫度將會持續飆高到40~50.




   

Thermal工作行為

    當溫度上升時要達到降溫的效果, 最直接的做法就是將熱源降低. 而在手機中主要的熱源有:
1. CPU
2. GPU
3. WLAN
4. LCD
5. Modem
6. Battery
7. Camcorder

主要熱源 :

上述所有的熱源最主要的就是CPU, Modem, Battery, Camcorder. GPU的部分雖然也是會熱, 但是與CPU相比卻小很多. 此外GPU通常能調整的範圍不多(通常只有3~4Freq.可選擇), 所以只要降GPU頻率, 使用者就會很容易就會感受到. 因此除非是真的過熱(表面超過40 ~ 41)才會對GPUthrottle.

Modem的部分也很類似, 當訊號不佳時Modem會打出高功率尋找基地台. Modemthrottle手段也只有降Txthroughput. 但是卻不會讓打Power找基地台的Modem降溫.

LCD方面的Throttle手段是降低LCD亮度, 效果也不錯, 但是使用者會感覺到畫面變暗. 因此目前QcomProject都沒導入. (因為有較多的控溫手段)

CPU :

CPU是手機內最熱的一個元件, 比較高階的CPU(例如89748939)如果用最高頻運行很輕鬆就會衝破100C. 因此CPU會是Thermal第一個需要調整的原件. CPU能夠被調整的也就是所謂的降頻, 當頻率被降低所產生的熱也會同步降低. 而每個Chip能調整的頻率不一定相同, 主要定義在CPU DCVS(dtsi). 這部分Thermal不會去修改, 主要由Performance Team主導.

qcom,msm-cpufreq@0 {
    reg = <0 4>;
    compatible = "qcom,msm-cpufreq";
    qcom,cpufreq-table =
        <  200000 >,
        <  400000 >,
        <  533330 >,
        <  800000 >,
        <  998400 >,
        < 1094400 >,
        < 1152000 >,
        < 1209600 >;
};
DCVS設定好後, 就可以在devices上得到資訊.

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
如此就可以知道目前有哪些CPU Freq.可以調整了.

GPU :

GPU又稱顯示晶片,是一種專門在個人電腦、工作站、遊戲機和一些行動裝置(如平板電腦、智慧型手機等)上實作圖像運算工作的微處理器. GPU分擔了部分原本是由CPU所擔當的工作, 尤其是在進行3D處理時, 功效更加明顯. 在手機方面GPU主要負責畫圖&圖形運算, 因此不會像一般電腦顯卡一樣這麼有力, 通常提供的GPUFreq.都不會太高. MSM8916為例GPU的部分能調整的頻率有400MHz, 310MHz 200MHz. 此外在玩遊戲時會大量地使用到GPU的資源, 如果這時候對GPU做降頻的話, 使用者很有可能就會感覺到遊戲會變卡頓. 因此GPU這部分除非溫度快到超過SPEC的範圍, 否則基本上都不會主動降頻.

qcom,gpu-pwrlevels {
    #address-cells = <1>;
    #size-cells = <0>;

    compatible = "qcom,gpu-pwrlevels";

    qcom,gpu-pwrlevel@0 {
        reg = <0>;
        qcom,gpu-freq = <400000000>;
        qcom,bus-freq = <3>;
    };

    qcom,gpu-pwrlevel@1 {
        reg = <1>;
        qcom,gpu-freq = <310000000>;
        qcom,bus-freq = <2>;
    };

    qcom,gpu-pwrlevel@2 {
        reg = <2>;
        qcom,gpu-freq = <200000000>;
        qcom,bus-freq = <1>;
    };

    qcom,gpu-pwrlevel@3 {
        reg = <3>;
        qcom,gpu-freq = <19200000>;
        qcom,bus-freq = <0>;
    };
};

詳細的頻率可以從下面的node得到.
cat /sys/class/kgsl/kgsl-3d0/gpu_available_frequencies

Battery :

Thermal對電池會做的動作是限制電池的充電電流, 停止快充. 最主要會發熱的因素是快充. 最近的Project開始在導入快充, 很容易發現在快充時手機會明顯發燙, Suspend下大約表面會到37度左右. 如果一邊快充又一邊使用手機, 溫度可能會來到40度左右. 此時CPU會被降頻, 使用者在操作上就可能會覺得卡頓. 能傳送的訊息為0, 1, 2, ...等數值, 而後續要做甚麼是Battery的同仁去實現, 例如停快充, 限制充電電流. Thermal這邊需要做的就是要溝通好並確認每個值對應的Action是對的.

Camcorder :

CamcorderThermalCamera發出的通知. 當開啟Camera, Camera會到Thermal這邊註冊Client. 當達到需要對Camera做控制時就會發送EventCamera那邊. 能傳送的值為0 ~ 10, 而定義要做甚麼事則是在Camera Driver那邊決定, 可以限制FPS在多少等等. 因此CamcorderAction定義, 每個Project有可能會不一樣.


Thermal運行架構   :

Qcom的架構主要由三個部分組成, BTM, KTM, ThermalEngine.
這三個分類主要是為了整個開機到開機完成階段而分成的三個Level.

2013年1月6日 星期日

記憶體

每當宣告變數時,電腦便在記憶體中的某處為它建立空間. 如果在函式(像main())中宣告變數,電腦會將它儲存在堆疊(stack)的記憶體區段中. 如果在函式以外宣告的全域變數,它將被儲存於記憶體的全域(globals)區段.

Stack(堆疊)
用於儲存區域變數的記憶體區段. 每當呼叫函式時,該函式的所有區域變數都會在堆疊上被建立. 每當進入函式時,區域變數被疊上去,當離開時,區域變數就從上面被拿掉. 要注意的是,堆疊的運作方式是從上往下發展的,它從記憶體的頂端開始並往下成長.

Heap(堆積)
Heap是針對動態記憶體(dynamic memory)而設的. 在程式執行時被建立的資料片段,程式動態的索取空間(malloc)並在使用完畢後須自行釋放該空間(Free).

Globals(全域區段)
全域變數是存在於所有函式之外的變數,在整個程式中都可被看到.

Constants(常數區段)
常數也是在程式一開始執行時被建立,但它們被儲存在唯讀記憶體.

Code(程式碼區段)
許多作業系統都將程式碼配置在最低的記憶體位址,程式碼區段也是唯讀的.

深入淺出C break & continue

break:
break陳述式被用來中斷迴圈及switch陳述式 <當你使用break時,務必知道你中斷的是甚麼>
break陳述式將中斷程式流,直接跳到當前迴圈之外,略過迴圈主體剩餘的部分.


continue:
如果想略過迴圈主體的其餘部分,並且回到迴圈的開頭,那麼continue陳述式是你的選擇

2012年11月21日 星期三

LDD3 scull_load 無法編譯 修改方法

 [LINUX] 在ubuntu環境編譯 skull 的方法
編譯LDD3書上範例 skull時,遇到了一些問題,茲記錄如下:

程式碼範例下載:http://www.oreilly.com.tw/product_linux.php?id=a184
測試機器編譯環境:Linux 2.6.32-45-generic #Ubuntu 10.04
書上的範例是適用於 Linux 2.4/2.6 版本的,因此在之後版本的核心下編譯會造成許多錯誤訊息,以下針對scull的編譯錯誤作一整理說明。

1. 缺少linux/config.h,

    原因:因為 config.h 在 linux kernel 2.6.19已經拿到 config.h
    改法:有兩種方式
    a. 在檔案內直接註解 #include <linux/config.h>
    b. 自行建立一個空的檔案,例如執行下列命令
    touch /usr/src/linux-headers-`uname -r`/include/linux/config.


2. scripts/Makefile.build:49: *** CFLAGS was changed in "/home/albert/examples/scull/Makefile". Fix it to use ccflags-y. Stop.

    改法:有兩種方式:
    a. CFLAGS -> EXTRA_CFLAGS
    b. make KBUILD_NOPEDANTIC=1



3. 錯誤: 初始值設定項裡有不明的欄位 「ioctl」

    linux內核到2.6.36之後把ioctl這個成員給移除了,改用了以下兩名新成員

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

    此處需更改兩個地方

    a. 更改 file_operations 的 ioctl

        struct file_operations scull_sngl_fops = {
        .owner = THIS_MODULE,
        .llseek = scull_llseek,
        .read = scull_read,
        .write = scull_write,
        //.ioctl = scull_ioctl,
        .unlocked_ioctl = scull_ioctl,
        .open = scull_s_open,
        .release = scull_s_release,
        };

    b. 更改 scull_ioctl 的函數原型 (main.c scull.h)

        //int scull_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
        long scull_ioctl( struct file *filp, unsigned int cmd, unsigned long arg)


4. 隱含宣告函式 「init_MUTEX」 [-Werror=implicit-function-declaration]


    在新版本的linux?核中,已經廢除init_mutex,新版本直接使用sema_init()。原始的定義如下 (linux 2.6.xx 時仍存在此定義)

        #define init_MUTEX(sem) sema_init(sem, 1)
        #define init_MUTEX_LOCKED(sem) sema_init(sem, 0)

    因此可更改如下:

        //init_MUTEX(&scull_devices[i].sem);
        sema_init(&scull_devices[i].sem, 1);


5. 錯誤: 「TASK_INTERRUPTIBLE」 undeclared (first use in this function)

    此常數定義在 sched.h
    需在 access.c 與 pipe.c 加上 #include <linux/sched.h>  即可


6. 找不到 SPIN_LOCK_UNLOCKED’

    原因:SPIN_LOCK_UNLOCKED has been deprecated since 2.6.19 and will get removed in 2.6.39。
    改法:
    // static spinlock_t scull_u_lock = SPIN_LOCK_UNLOCKED;
    static DEFINE_SPINLOCK(scull_u_lock);


7. 錯誤: 「struct task_struct」 沒具名為 「uid」 的成員

    原因:struct task_struct 已經重新定義
    改法:

        current->uid 改成 current->cred->uid
        current->euid 改成 current->cred->euid
        current->comm 改成 current->cred->comm

Hello World module 安裝


建立root密碼:
  • 1. 安裝完Ubuntu的時候,第一次使用root會提示要你輸入password,你會發現你自己根本沒有設置password。如果你要使用和Unix一樣使用root帳戶,解決辦法是:
  • 1.1. 在終端輸入 sudo passwd root (注意是passwd不是password)這樣你就可以通過 su 登錄成為root,它會提示要求password,這時候你就輸入你當前帳戶的密碼。然後它會提示你輸入Enter new UNIX password 和 Rtry new UNIX password。那麼你就新建一個root賬戶的密碼就可以了,看見password updated successfully就成功了。
編寫hello.c

編寫Makefile 
將hello.c與Makefile放在相同資料夾下
%make
%su
%insmod ./hello.ko
%lsmod
%dmesg
%rmmod hello
%dmesg

當然,如果你使用的是ubuntu,以上命令前要加sudo。打入 sudo insmod demo.ko,輸入管理者密碼即可成功掛載。當然畫面上不會出現任何訊息,因為printf是寫入核心態,我們的畫面是使用者態,核心的printf只 能從log或dmesg命令查看,因此打入dmesg,就可以看到掛載訊息了。至於相同的,卸載就打入 sudo rmmod demo,從dmesg也可以看到成功卸載的log。

字元驅動程式筆記

1. dev_t 型別
(1)在核心中,dev_t型別(定義在linux/types.h 中)用來保存裝置編號 - 包括major和minor。
(2)在核心 2.6.0中,dev_t是一個『32位的數』,其中12位表示major,20位表示minor。


(3)獲得dev_t的major、minor:
  MAJOR(dev_t dev);
  MINOR(dev_t dev);
(4)轉換成dev_t 類型:
  MKDEV(int major, int minor);

2. 向kernel註冊
(1)include linux/fs.h
fs.h 是編寫device drivers 所需要的header file. 許多重要的函數和數據結構在此定義.
(2) 註冊函數
事先設定major==> int register_chrdev_region(dev_t first, unsigned int count, char *name)
動態分配major==> int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)
反註冊都相同 ==> void unregister_chrdev_region(dev_t first, unsigned int count);
ps: 核心2.6以前的方法 ==>int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
2.6也還能用,但效能彈性有限。
而反註冊方法===> int unregister_chrdev(unsigned int major, const char *name);


3. cdev 結構管理的函數, 它代表内核中的字元設備.
(1)include linux/cdev.h
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *dev, struct file_operations *fops);
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
void cdev_del(struct cdev *dev);


4. file_operations 結構
這個結構定義在linux/fs.h中,用來建立驅動程式操作到設備編號的連接。
它包含了一组函數指標,每個打開的文件(在內部用file表示)和一组函數關聯(通過包含指向一個file_operations结構的『f_op』字段)。


5. file结構
stuct file定義在linux/fs.h中,是一個內核結構,它不會出現在用戶程序中。
(與用戶空間程序中的FILE结構沒有任何關聯,FILE不會出现在內核代碼中。)
   file结構代表一個打開的文件(不僅限於設備驅動程序,系统中每個打開的文件在內核空間中都有一個對應的file结構)。
然而,指向『struct file』的指標通常稱為『flip』。


6. read和write方法
(1)ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);

(2)參數filp是文件指標,count是請求傳輸的數據長度,buff是指向用戶空間的緩衝區,
offp是一個指向“long offset type”對象的指標,
它指明用戶在文件中進行存取操作的位置。
返回值是“signed size type(有符號的尺寸類型)”。

(3)read和write方法不能直接引用buff參數(用戶空間)的內容,
安全訪問應該通過『内核提供的專用函數』完成:(定義在asm/uaccess.h中)
unsigned long copy_to_user(void __user *to,const void *from,unsigned long count);
unsigned long copy_from_user(void *to,const void __user *from,unsigned long count);

(4)read和write方法的返回值
http://wiki.magiclinux.org/ftp/haulm/book/Device/ch03s07.html (3.7.1/2)
其中『負值』意味著『錯誤』,錯誤碼 在linux/errno.h中定義,
如:-EINTR(系统調用被中斷)-EFAULT(無效地址)