顯示具有 Linux 標籤的文章。 顯示所有文章
顯示具有 Linux 標籤的文章。 顯示所有文章

2012年2月20日 星期一

TI OMAP3 DM3730 USB host controller high full low speed?

因為 TI OMAP3 DM3730 在 USB host controller 上偵測不到 full/low speed USB device,經查之後發現目前的硬體接線 ULPI 因為 DM3730 跟選用的 USB transceiver 的ULPI phy 的 serial 排線順序不一樣,在設定為外接 USB transceiver phy,而非 TLL 的接線方式時,無法使用 full/low speed mode,選用 device 時必須注意只能接 high speed device,如果要保持原有 

USB host controller ---phy--- USB transceiver --D+D-

接法, ulpi phy 線路不改而想要使用 full/low speed USB device,可以再外接一顆 high speed USB hub 解決。

2012/02/24

今天又花了些時間跟 Ken,還有 EE 確定 USB 問題,有些地方一開始誤解,因為是從軟體驅動程式行為,發現硬體有問題,一開始以為是目前元件接法

USB host controller ---phy--- USB transceiver --D+D-

無法實作, EE 在我提出有問題後就進行調查,發現是 ulpi serial 無法對應,其實並非這樣子的元件接法問題,而是 DM3730 的輸出分 ulpi parallel, ulpi serial, TLL 三種,因為我們有接 transceiver,就只能選擇 transceiver 有支援的 ulpi parallel 或 ulpi serial二擇一,high speed 的接法是 ulpi parallel,而 full/low speed 的 ulpi serial 接法,因需要接腳比 parallel 少,原本可以使用同一組硬體排線,但是因為 DM3730 跟所用 transceiver 的 ulpi serial 接腳順序與 transceiver 不同,造成無法使用現有的線路,待跳線驗證後可確定。

結論:

跳線加上 ohci driver 實測驗證後發現以 3 wire 的 ulpi serial 對 usb transceiver 就已經會發生誤判為有 USB low device (MiniPCI-E 上沒插卡),但我們 usb host port 的 transceiver 又沒有介面控制調整為 3-wire serial, 6-wire serial 或 12(8+4) parallel(預設),再加上 DM3730 SoC 原有的 ehci 轉換到 ohci 就會鎖死的 bug,最後確定無法使用原來的 

DM3730 USB host controller -- phy -- USB transceiver -- D+D- 

的接法,必須要再外接一個 USB hub 在 MiniPCI-E 上才能使用 USB full/low speed device.

相關文章:

2011年8月16日 星期二

Linux gpio & irq mapping

arch/arm/plat-pxa/gpio.c 定義了 pxa_gpio_chip 指的是某一個管理 gpio interrupt 的 register 而一個 register 是 4 bytes,所以最多管 32 根 gpio (irq),例如 pxa270 的 Section 24. Interrupt Controller 定義了 0~39 (扣除 reserved 也用到 33,第 34 根 GPIO),因此保留兩個 gpio_chip 的空間 32*2= 64 個位置給 gpio 對應的 irq number。

Interrupt 借由 Interrupt controller 向 pxa270 發出 request,取得該 pin 腳的 GPIO number,再透過 arch/arm/mach-pxa/include/mach/gpio.h 的 gpio_to_irq 取得 irq number,再指向在

arch/arm/mach-pxa/pxa27x.c 中的 pxa27x_init_irq 與
arch/arm/plat-pxa/gpio.c 中的 pxa_init_gpio 初始化時指定的 gpio 與 irq chip,最後註冊 irq chip 內的 chained_handler ...

Linux 使用 irq_desc 表示某一個 irq,chip level ISR 實作可能是舊式的 __do_IRQ 或採用新的 irq_desc->handle_irq() ,到上一層透過 irq_desc->chip 呼叫不同的 handler

kernel/irq/handle.c 定義了 irq_desc[NR_IRQS], NR_IRQS, nr_irqs 代表目前 Linux kernel 擁有的 irq 總數

另外相關的部份:

arch/arm/plat-pxa/gpio.c

arch/arm/kernel/calls.S 處理的 system call interface

arch/arm/kernel/entry-armv.S 定義 irq_handler, interrupt vector table, dispatcher table

arch/arm/mach-pxa/irq.c 處理的 irq interface

https://www.kernel.org/doc/htmldocs/genericirq/index.html

linux kernel source base: 3.1.0-rc1

參考:

ARM中斷流程

2011年8月7日 星期日

linux nfc kernel driver pn544

讀 linux kernel "Documentation/nfc/nfc-pn544.txt"

感謝作者: Jari Vanhala
聯絡方式: Matti Aaltonen (matti.j.aaltonen at nokia.com)

兩個可參考的驅動程式: "drivers/nfc/pn533.c", "drivers/nfc/pn544.c" 感謝 Nokia 的愛心。

使用 linux kernel 的 misc device, device name /dev/pn544
其中 pn544 有 I2C, SPI, HSU 三種介面但驅動程式只提供 I2C 介面實作

對 user space 介面有 sysfs 的硬體測試與 IOCTL 切換 HCI, firmware update 兩種工作模式,傳輸訊息的方式有三種:read, write, poll

由 user space 傳訊息到 PN544  晶片進行控制,驅動程式目前只傳遞訊息不瞭解訊息內容

溝通協定

正常的 HCI 模式與 firmware update 模式的 read/write 行為不太一樣,因為兩種模式的訊息格式與溝通協定都不同。

HCI模式的溝通協定採用 ETSI 的 HCI spec,firmware update 則另有特殊協定。

HCI 訊息有 8 bit header 跟訊息內容。header 包括訊息長度,最大 33 bytes。傳送出去的訊息需要經過 checksum 驗證。

Firmware update 訊息長度存在第二(MSB)跟第三(LSB)位元組,最大值 1024 bytes.

sysfs 的硬體測試指的是讀取 sysfs 檔案後能將 pn544 轉為 firmware update mode,如果未提供測試則不需要建立此 sysfs 檔案。

使用範例:

 60 Example:
 61 > cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test
 62 1

正常運作流程:
device 檔案 open 後 pn544 就給電,未開啟前斷電,一次只能有單一使用者。user space 應用程式使用 HCI 訊息控制。 當有資料可讀時就發出一次中斷,當 user space 下 read 時才從 pn544 實際讀取資料,poll() 檢查 read interrupt state。設定與單元測試都由 user space 的 read/write 完成。

範例 platform data:


78 static int rx71_pn544_nfc_request_resources(struct i2c_client *client)
79 {
80 /* Get and setup the HW resources for the device */
81 }
82
83 static void rx71_pn544_nfc_free_resources(void)
84 {
85 /* Release the HW resources */
86 }
87
88 static void rx71_pn544_nfc_enable(int fw)
89 {
90 /* Turn the device on */
91 }
92
93 static int rx71_pn544_nfc_test(void)
94 {
95 /*
96 * Put the device into the FW update mode
97 * and then back to the normal mode.
98 * Check the behavior and return one on success,
99 * zero on failure.
100 */
101 }
102
103 static void rx71_pn544_nfc_disable(void)
104 {
105 /* turn the power off */
106 }
107
108 static struct pn544_nfc_platform_data rx71_nfc_data = {
109 .request_resources = rx71_pn544_nfc_request_resources,
110 .free_resources = rx71_pn544_nfc_free_resources,
111 .enable = rx71_pn544_nfc_enable,
112 .test = rx71_pn544_nfc_test,
113 .disable = rx71_pn544_nfc_disable,
114 };


NXP pn544 introduction pdf
including: contact less procotols, host interfaces, power cosumption

NXP NFC 的 peer to peer, card emulation, Reader三種模式與使用的溝同協定說明

值得注意的是,就專利版圖來看,目前 NFC 市場並沒有真正的大廠,前六強分別是 Sony, Broadcom, IBM, Nokia, Panasonic, NXP

2011年7月27日 星期三

Linux kernel spinlock 使用方式與時機

由 Linus 親自整理的 Linux kernel spinlock 文件

~linux/Documentation/spinlocks.txt
            多核心處理 critical section 的方式,safe but slow(disable interrupts)
 67 See . The basic version is:
 68
 69    spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED;
 70
 71
 72     unsigned long flags;
 73
 74     spin_lock_irqsave(&xxx_lock, flags);
 75     ... critical section here ..
 76     spin_unlock_irqrestore(&xxx_lock, flags);

            單核心處理 critical section 的方式:
115
116     spin_lock_irqsave(flags);
117     .. critical section ..
118     spin_unlock_irqrestore(flags);
119

        reader/writer 處理 shared data 的鎖定方式:
149    rwlock_t xxx_lock = RW_LOCK_UNLOCKED;
150
151
152     unsigned long flags;
153
154     read_lock_irqsave(&xxx_lock, flags);
155     .. critical section that only reads the info ...
156     read_unlock_irqrestore(&xxx_lock, flags);
157
158     write_lock_irqsave(&xxx_lock, flags);
159     .. read and write exclusive access to the info ...
160     write_unlock_irqrestore(&xxx_lock, flags);

        如果確定不會跑在 interrupt handler 中(否則會重複鎖定造成 dead lock),
        可以用簡化的 spin_lock API 較快
192     spin_lock(&lock);
193     ...
194     spin_unlock(&lock);

        因此 read lock 等可以用簡化的非 irq safe spinlock,但 write lock 則必須
        使用 irq safe 的 spin lock

Linux kernel and user space interfaces

1. /dev/  character device ioctl control code read/write interface
2. /sys sysfs  show/store read/write interface
3. /proc procfs  read/write interface
4. event: input events (for example key events)

2011年7月21日 星期四

修改 linux device driver 載入 順序

1.  platform driver 可以調整 platform device 的 register 順序
 
2.  強者同事 Gary 使用 include/linux/init.h 定義不同順序的 init macro,module_init default 是 order 6,可以設定 late_initcall 是最後的 order 7

3.  build  kernel module, 在 user space 調整 module loading 順序

2011年6月26日 星期日

DISCONTIGMEM on PXA270

Russell King在回覆給 dera 的 DISCONTIGMEM on PXA270 一文中,提到 PXA270 平台設定
多個 DRAM memory bank 在 Linux kernel 的設計方式:

1. PHYS_OFFSET 從最小的 SDRAM 開始,在該文中是以 0x8000,0000
2. 因為 memory bank 之間有太大的漏洞,必須修改  __virt_to_phys/__phys_to_virt 將你的實體
記憶體壓縮排列到虛擬記憶體中。
3. 因為實體與虛擬記憶體非 1:1 對應,因此不能定義 NODE_MEM_SIZE_BITS, 而是要以單一
bank (該文是 128MB) 設定 NODE_MEM_SIZE_MASK (128MB -1)
4. 定義 KVADDR_TO_NID(vaddr) 將虛擬記憶體轉為 node number
5. 定義 PFN_TO_NID(pfn)轉 page frame number (phys >> PAGE_SHIFT) 到同一
node number (memory bank)
6. 公式就是以下列式必須符合 SDRAM 每一個位址:
KVADDR(vaddr) == PFN_TO_NID(phys_to_pfn(virt_to_phys(vaddr)))
7. Vernon Sauder 在回文中提到 必須考慮到 arch/arm/mach-pxa/Makefile.boot
中定義的 linux kernel start address zreladdr, 有修改為 0x8000,8000.

但實務上現在 arm linux 不應該再使用 DISCONTIGMEM ,而該使用 SPARSEMEM
1. mach/memory.h 定義 MAX_PHYSMEM_BITS 跟 SECTION_SIZE_BITS
可以在 arch/arm/include/asm/sparsemem.h 找到這兩個參數的定義

2.  config 中加入 ARCH_SPARSEMEM_ENABLE 移除 ARCH_DISCONTIGMEM_ENABLE
3. 仍需要修改  __virt_to_phys/__phys_to_virt 將你的實體記憶體壓縮排列到虛擬記憶體中。
4. 確定 sparse memory 的 patch 有上, finish of sparse memory patch

2011年6月19日 星期日

ARM Linux kernel 記憶體配置

摘譯這篇由 Russell King 在 2005 年 11 月 17 日針對 2.6.15 Linux kernel 撰寫的 ARM Linux kernel 記憶體配置

裡面指的是 linux kernel 使用 arm cpu 的虛擬記憶體的位址配置與排列方式

arm cpu 的虛擬記憶體定址空間: 4GB (32 位元的定址空間, 0000,0000 ~ FFFF,FFFF)

沒有遵循這個記憶體配置的 kernel 可能無法開機或發生隨機當機


===========開始=========針對 user space 的加入內容==========================

針對每個 user space 程式而言,其實都跟硬體裝置的記憶體映射空間, kernel 使用的記憶體空間,三者共用整個 4GB 的虛擬記憶體。

user space 程式這個區塊內部的記憶體配置如下:
   +----------------------+---------------------+-----------+------------+------------+
    | Code segment(r+x) | Data segment(r+w) | BSS(r+w)  | Heap(r+w) | Stack(r+w) |
   +-------------------------------------------------------------------------------------+

Code segment 是 instruction memory,儲存程式碼
Data segment 是 data memory,使用 brk(), sbrk() system call 改變或查詢 Program break 大小
BSS segment 是未初始化的 global 變數存放位置
Heap segment 儲存程式執行期產生的動態變數、記憶體區塊由 malloc() 取用,free()  釋放
Stack segment 使用 SP stack pointer 暫存器輔助 IP instrcution pointer 指定,由 function call, 與其 local 變數使用 

註:參考 Memory Management for System Programmers (pdf)

在 Linux 系統底下我們可以藉由 /proc/some_pid/maps 看到執行檔與 library memory map,另外 smaps 則詳列各區段 memory map 的使用狀態。其中可以看到從  0x0000,1000~0xBF00,0000 的載入函式庫與 heap, stack, 其中 stack 與 TASK_SIZE 相鄰

舉某個 android process 為例:
其中

    0xBEBE,9000- 0xBEBF,E00 是 stack 使用
    0xB000,0000 ~ 0xB001,0000 是 linker 載入位置, Thread 0 Stack
    0x8000,0000 ~ 0xAFE46000  是 Non-prelinked libraries
    0x4154,C000 ~ 0x8000,0000 是 mmap'd 記譯體,Java jar library, apk 檔案
    0x4000,0000 ~ 0x4154,C000 是 mmaped open file
    0x0000,A000 ~ 0x003C,4000 用在 heap
    0x0000,1000~0x0000, A000 佔用 .text (code segment) / .data 

Android 2.3 以前的 prelink library build/core/prelink-linux-arm.map 裡面除了重述 Russel King 的 ARM Linux kernel memory 的記憶體配置,Android 的記憶體配置中,包含兩塊系統預設的 library memory mapping:

    其中 Android prelink 定義了以下兩大塊記憶體對應:

    # 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
    # 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
    可以在 prelink-linux-arm.map 中找到或新增你要加入的 library 記憶體對應。

    # 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
    0xAEF00000  - 0xAFF00000 # core system libraries
    0xAE700000 - 0xAEE00000 # bluetooth libraries
    0xAE300000 - 0xAE600000 # extended system libraries
    0xACA00000 - 0xAE200000 # core dalvik runtime support
    0xA9C00000 - 0xAC900000 # graphics
    0xA8D00000 - 0xA9B00000 # audio
    0xA7000000 - 0xA8B00000 # assorted system libraries
    0xA4800000 - 0xA6F00000 # pv libraries
    0xA4000000 - 0xA4700000 # opencore hardware support
    0xA3800000 - 0xA3900000 # pv libraries
    0xA2F00000 - 0xA3700000 # stagefright libraries
    0xA2A00000 - 0xA2E00000 # libraries for specific hardware

    # 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
    0x9CA00000 - 0x9F000000 # libraries for specific apps or temporary libraries

# 0xC0000000 - 0xFFFFFFFF Kernel
# 0xB0100000 - 0xBFFFFFFF Thread 0 Stack
# 0xB0000000 - 0xB00FFFFF Linker
# 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
# 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
# 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
# 0x40000000 - 0x7FFFFFFF mmap'd stuff
# 0x10000000 - 0x3FFFFFFF Thread Stacks
# 0x00000000 - 0x0FFFFFFF .text / .data / heap

====================針對 user space 的加入內容==========結束================

因為 cpu 架構的發展變化可能影響 kernel 記憶體位置的配置,user space 程式應該只使用到 0000,1000 ~ TASK_SIZE -1 這段記憶體。請使用 open(), mmap() 系統呼叫使用這段記憶體。

    FFFF,8000 ~ FFFF,FFFF           copy_user_page/clear_user_page 使用
    SA11xx 跟 Xscale cpu 建立 minicache 的記憶體位址

    FFFF,1000 ~ FFFF,7FFF           保留
    各平台禁止使用這塊記憶體配置

    FFFF,0000 ~ FFFF,0FFF           cpu vector page
    如果 cpu 支援改變中斷向量位址 (control register V bit), 會把中斷向量映射到這
    4K bytes 區塊

    FFC0,0000 ~ fffe,ffff           DMA 記憶體映射區塊,由 dma_alloc_xxx 的
    函式呼叫都使用這塊記憶體動態配置,總共 0x003F,0000 佔 4MB

    FF00,0000 ~ FFBFF,FFFF          保留給 DMA 記憶體映射擴充使用 佔 12MB

    VMALLOC_END ~ feff,ffff         平台自由運用的記憶體區塊都建議用這段
    VMALLOC_END 必須對齊 2MB 的記憶體區塊
    在 arch/arm/mach-pxa/include/mach/memory.h 中定義

    #define ARM_DMA_ZONE_SIZE SZ_64M 除了以上 16MB 保留給 DMA 另外用了 48 MB
    使用 FBC0,0000~fffe,ffff
    在 arch/arm/mach-pxa/include/mach/vmalloc.h 定義
    #define VMALLOC_END       (0xE8000000UL)

    VMALLOC_START ~ VMALLOC_END - 1 vmalloc() / ioremap() space
    由 vmalloc() 及 ioremap() 取得的記憶體會動態配置在這區塊,VMALLOC_START 相依
    於 high_memory(不能有重疊)
    在 arch/arm/include/asm/pgtable.h 中註解有說明留 8MB 給誤動作的記憶體錯誤緩衝空間,
    定義如下:
    #ifndef VMALLOC_START
    #define VMALLOC_OFFSET      (8*1024*1024)
    #define VMALLOC_START       (((unsigned long)high_memory + VMALLOC_OFFSET) &
                                                      ~(VMALLOC_OFFSET-1))
    #endif

其中 high_memory 在 arch/arm/mm/init.c 的 void __init bootmem_init(void) 計算:
    high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1;

    PAGE_OFFSET ~ high_memory - 1   Kernel direct-mapped RAM region
    這段記憶體對應到平台上的實體記憶體,通常跟系統記憶體是 1:1 的對應
    (direct addressing) 在 arch/arm/include/asm/memory.h 看到 PAGE_OFFSET 定義為
    UL(CONFIG_PAGE_OFFSET)
    也因此我們可以在 arch/arm/include/asm/memory.h 看到以下 virtual, physical 記憶體互轉的
    巨集定義:
    #define __virt_to_phys(x)   ((x) - PAGE_OFFSET + PHYS_OFFSET)
    #define __phys_to_virt(x)   ((x) - PHYS_OFFSET + PAGE_OFFSET)

    其中的 PHYS_OFFSET 實體記憶體在 cpu 定址的記憶體初始(start of bank 0 dram)位址
    ,是定義在同檔案的以下巨集:
    #define PHYS_OFFSET UL(CONFIG_DRAM_BASE) 與
    #define PHYS_OFFSET PLAT_PHYS_OFFSET
    CONFIG_DRAM_BASE 是實體記憶體起始的 offset 值,在 pxa 平台可以
    在 arch/arm/mach-pxa/include/mach/memory.h 中看到定義為:
    #define PLAT_PHYS_OFFSET    UL(0xa0000000)
    我們可以對照 pxa270 spec memory map 章節的 dram map 起始位址
    0xa000,0000 作為實體記憶體的起始位址得到驗證。
    再配合 arch/arm/Makefile 定義的 Linux kernel image text area 值
    TEXT_OFFSET := $(textofs-y)
    而 textofs-y   := 0x00008000 是預設值。
    arch/arm/boot/Makefile 告訴我們 zImage 等 target 就是
     21 #   ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
    因此可以算出:ZRELADDR = PHYS_OFFSET + TEXT_OFFSET = 0xA000,8000 (physical)
    而虛擬位置是 0xC000,8000(virtual),這個計算結果可以跟 Makefile include 的
    include $(srctree)/$(MACHINE)/Makefile.boot 檔案,在 pxa 平台就是
    arch/arm/mach-pxa/Makefile.boot 檔案裡面定義的 zreladdr-y
    zreladdr-y   := 0xa0008000
    在實務上我們也可以修改 zreladdr-y 定義的實體記憶體位址,來改成我們想要把 linux kernel
    起始點放在 physical memory 的哪一個位置上。這也就是為何我們從 boot loader 載入 linux
    kernel 都要指定到某個固定實體記憶體位址的原因,因為在 make kernel 時就決定了這個位址。

    PAGE_OFFSET 也就是 linux kernel image 的虛擬位址起始點, 0xC000,0000 3GB 的 user address
    space

    TASK_SIZE ~ PAGE_OFFSET - 1     Kernel module space
    使用 insmod 動態載入的 kernel modules 會在這段記憶體使用動態配置
    arch/arm/include/asm/memory.h 定義
    #define PAGE_OFFSET     UL(CONFIG_PAGE_OFFSET)

    0000,1000 ~ TASK_SIZE - 1       User space mapping
    每個 thread 使用 mmap 對應到這段位置
    arch/arm/include/asm/memory.h 定義
    #define TASK_SIZE         (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000))
    如果 PAGE_OFFSET = 0xC000,0000,則 TASK_SIZE = 0xBF00,0000 (3GB - 16MB)

    0000,0000 ~ 0000,0FFF           cpu vector page, null pointer trap
    不支援中斷向量重新映射的 cpu 會把中斷向量 page 放在這 4K bytes

關於以上作業系統的 Virtual address map 我們可以使用 arch/arm/mach-pxa/generic.c 與 arch/arm/mach-pxa/include/mach/hardware.h 的建立 memory mapped device 跟 chip select memory mapped device 的 Virtual -> Physical mapping

arm 因為使用到 co processor 15,controller register(1th), v bit (13th), 因此用到 vector page relocate 功能,原本會到 0x0000, 0000 找 vector table 的動作改為到 0xffff, 0000 找 vector table,而我們在 mmu 啟動後使用的 0xffff,0000 Virtual address 就是在 arch/arm/mm/mmu.c 的 devicemaps_init() 中將 Linux kernel 開機時在 arch/arm/kernel/entry-armv.S 建立的 vector table 存在 SDRAM 後,建立一組 0xffff, 0000 high vector page 對映到實體記憶體的 vector table,因此 Linux kernel 開機啟動 mmu 進入 virtual address mode 後才能在硬體發出 exception 中斷時,找到 vector table 處理中斷。

2010年5月29日 星期六

linux USB hid driver cypress_m8.c

參考 USB Device Class Definition for Human Interface Devices(HID)
firmware spec version 1.11,範例 driver 是 USB Cypress M8 driver
~linux/drivers/usb/serial/cypress_m8.c,source code 中說有
文件就在 ~linux/Documentation/usb/usb-serial.txt。

主要是看 DeLorme Earthmate USB 的 usb hid 如何設定,她使用的
是 feature report 的方式,在 control pipe 傳送 5 bytes 資料。
對應到 USB HID spec 的 7.2 Class-specific Requests 裡面的 7.2.1
Get_Report Request 跟 7.2.2 Set_Report Request,透過 control
pipe 傳送 host <-> device 資料。

呼叫 usb_control_msg(),定義在 USB core 的實作中
~linux/drivers/usb/core/message.c:


/**
* usb_control_msg - Builds a control urb, sends it off and waits for completion
* @dev: pointer to the usb device to send the message to
* @pipe: endpoint "pipe" to send the message to
* @request: USB message request value
* @requesttype: USB message request type value
* @value: USB message value
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
* @timeout: time in msecs to wait for the message to complete before timing
* out (if 0 the wait is forever)
*
* Context: !in_interrupt ()
*
...
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index, void *data,
__u16 size, int timeout)



有兩種用法,差異在於傳輸方向,是以 host 為主體而有 in/out, set/get 不同。
一個是 host 傳到 device(out/set),另一個是 device 到 host(in/get)。

host->device 是 USB_DIR_OUT,使用 HID_REQ_SET_REPORT request
建立 send endpoint pipe 是用 usb_sndctrlpipe()

device->host 是 USB_DIR_IN,使用 HID_REQ_GET_REPORT request
建立 receive endpoint pipe 是用 usb_rcvctrlpipe()


retval = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
0x0300, 0, feature_buffer,
feature_len, 500);

retval = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
HID_REQ_GET_REPORT,
USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
0x0300, 0, feature_buffer,
feature_len, 500);


第一個參數就是 struct usb_device *dev,probe 時就可以取到
第二個是利用 MACRO 從 usb device 找到 usb device 的 endpoint pipe
第三個參數是 message request value,HID_REQ_SET_REPORT 或
HID_REQ_GET_REPORT
第四個是 message request type value,
USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS, 或
USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,

第五個參數是 message value 看 device hard code: 0x0300
第六個參數是 message index value hard code: 0

因為採用的是 feature report 的方式傳送 5 bytes 資料存在
第七個參數 feature_buffer 中,
第八個參數是 feature_len 是資料長度。
第九個參數是 500 micro seconds 的 timeout。

相關文章:

TI OMAP3 DM3730 USB host controller high full low speed?



相關連結:

http://www.usb.org/developers/devclass_docs/HID1_11.pdf

~linux/drivers/usb/serial/cypress_m8.c

~linux/drivers/usb/core/message.c

2010年5月28日 星期五

linux kernel read_proc() in procfs

在 ~linux/fs/proc/generic.c 中找 __proc_file_read() 的實作可以看到相關說明:
/*
* How to be a proc read function
* ------------------------------
* Prototype:
* int f(char *buffer, char **start, off_t offset,
* int count, int *peof, void *dat)
*
* Assume that the buffer is "count" bytes in size.
*
裡面有三種讀 read_proc 的方式說明,底下就是說明的程式碼,
也可以搜尋其他 driver使用 read_proc 的範例。
比如 ~linux/drivers/char/efirtc.c。
*/
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
...
這邊是當 kernel 讀取 proc 檔呼叫 driver read_proc callback 的地方,也就是用法。因此可以倒推回去我們的 callback function 如何實作:

第一種是 driver 要回傳的資料量小於 read_proc 中的 buffer size(PAGE_SIZE)。
此時不管 *start,將 driver 要傳給 filesystem 的資料擺在 buffer 的 offset 位置,我們只看 offset 跟上次 callback 讀到的 buffer size, n 的差值,在接到的 offset 跟剩餘要傳的 n 差值(n - offset) 為零時,告知 filesystem 我們的 driver 已經傳完資料,才設定 *peof = 1; 否則繼續 callback。

第二種是當你有大量資料要傳輸時使用。
將 *start 設定為介於 buffer 跟零 之間的值,資料是從 buffer 起始位址開始填,return 每次要填的資料長度,如果你的資料還沒填完,第二次進入 read_proc 時 offset 值會增加 *start 的長度,直到你填完資料後再設定 *peof = 1; 就可以完成傳輸大量資料了。

第三種跟第一種一樣,只是把 offset 代換成 *start

參考文獻:
  1. procfs-guide.pdf 3.1 Reading data
  2. linux kernel source: ~linux/fs/proc/generic.c, ~linux/drivers/char/efirtc.c
  3. Linux Modules (3) - procfs

2010年5月27日 星期四

boot code, bootloader, and Linux Kernel entry points

菠蘿麵包整理了 Android 開機流程 boot sequence
給個 boot rom, bootloader, Linux kernel, Android Init, Zygote, Dalvik,, System Server, 最後發出
ACTION_BOOT_COMPLETED 的 braodcast intent 觸發需要知道開機啟動的服務,其中從
硬體到使用者接觸到的應用程式的完整流程。

我們關注的是前面這段觀念:
硬體平台上的 boot ROM 儲存了 boot code,boot rom 本身也是在記憶體上執行,arm cpu
藉由 reset 0x0000, 0000 由 chip select 線路連結的開機媒介決定到哪一種 storage (Nor flash,
 Nand flash) 去讀取 boot loader。 將 boot loader 載入到實體記憶體後開始執行 boot loader ...

booting sequence documentation 則提到在 bootloader 到 linux kernel 端的開機介面
與流程:

ARM Linux kernel 對 bootloader 所需要的,就是藉由 bootloader 將 Linux kernel 從
storage 讀出載入到記憶體,設定某些暫存器,並呼叫 Linux kernel entry point,此
時還不需要啟動 MMU。

 Linux kernel 的 zImage 壓縮檔,就必須要跳到 arch/arm/boot/compressed/head.S
會使用  arch/arm/boot/compressed/misc.c (抄自 gzip 的某些函式介面) 裡面的
decompress_kernel() 其中會呼叫 arch_decomp_setup() 設定 debug FF/ST uart port,
或特殊的 Chip Select memory mapping,接著在開機印出 "Uncompressing Linux..."
字串後進行解壓縮。

ARM Linux boot sequence

另外關於 arm linux 的開機流程也有人寫了程式碼分析文:
ARM Linux boot sequence

zImage 解壓縮階段:
從跟 boot loader 串接,關閉 cpu cache, MMU, 設定 stack, kernel entry point 在實體
記憶體位置,設定 cpu 型號辨識,啟用 cpu cache, MMU, 設定 MMU page table
將 kernel zImage 解壓縮並載入到 RAM,進入 kernel start...

ARM 相關的 kernel code:
查詢 cpu 型號,啟用多核心支援,查詢(主機)板子的 machine 型號,MACHINE_DESC
macro 的定義。建立 MMU 的 page table,enable MMU,初始化 cache, write buffer。
繼續 enable MMU,設定 page table pointer (TTB),找到 page table 起始點,切換
MMU 進入 virtual address space, 回到已經過 mmap 過的 switch data。

複製 data segment 到 RAM
清空 BSS(寫零)
跳到 start_kernel 開始執行 Linux kernel 開機程序。也就是接 Intel 文件中的 C 程式碼
起始點。 

Linux kernel entry points

Intel 的 device driver debugging 可以找到幾個 Linux kernel source entry point。

開發新平台(OS Adaptation, OS bring up, customize core components)
其中一項重要的工作就是寫驅動程式,有些多媒體編解碼的最佳化,
也是在驅動程式端實作。

一些有用的 kernel 資訊如:active kernel threads, loaded kernel modules

x86 開機流程中 BIOS->OS boot loader --start_kernel()-->OS

sched_init() 是 scheduler initialization

mwait_idle() 是 OS 主要的迴圈(就像 micro controller 的 main loop)


相關連結:

Device Driver Debugging on Intel Atom processor based devices (pdf)

2010年5月8日 星期六

從 gpio & I2C 硬體線路對應到檔案讀寫

0. 硬體電路的 schema
找到電路圖上的 gpio, i2c 線路圖上的 block 是接到 datasheet 上哪一個 slot

1. datasheet 中的 gpio chapter
針對 gpio controller 的 register 值依照 in/out, pull up/down, data 值設定

2. board initial entry point,比如 Samsung s3c6410
~linux/arch/arm/mach-s3c64xx/mach-smdk6410.c
MACHINE_START macro 定義 board information
可以看到註冊的 .init_machine 進入點 function 是 smdk6410_machine_init

Qualcomm 的 arm board initial 檔案則是在
~linux/arch/arm/mach-msm/board-msm7x27.c
定義了 QCT MSM7x2x (7225)
~linux/arch/arm/mach-msm/board-msm7x30.c
定義了 QCT MSM7x30
~linux/arch/arm/mach-msm/board-qsd8x50.c
定義了 QCT QSD8X50 (8250)

3. i2c
關於 i2c 參考 Documentation/i2c/instantiating-devices,可以看到 mach-smdk6410.c 定義兩個 i2c slot 0, 1 為 struct i2c_board_info,裡面定義這個 bus 上接的 i2c slave device(s) 名稱與 slave address 資訊,透過 S3C_EINT 對應到外部中斷 12,然後在 smdk6410_machine_init():

i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

註冊兩個 i2c bus,0 跟 1。接下來 i2c device 的具體化由 i2c core 完成。

kernel driver 可以在 ~linux/drivers/i2c 目錄找到,可以使用 i2c-dev,直接 sudo modprobe -a i2c-dev,就可以在 /dev/i2c-0 存取 i2c bus。

參考 ~linux/Documentation/i2c/dev-interface 可以針對 /dev/i2c-0[1] 的 device file 使用 user space driver 的 ioctl 控制 i2c 設定與傳輸。

最簡單的動作是 ioctl(file, I2C_SLAVE, addr),就直接對 file read(), write() 就可以讀寫 i2c device 資料。

4. gpio
關於取得需要使用的 gpio 方式如下:

gpio_request(S3C64XX_GPN(5), "LCD power");
gpio_request(S3C64XX_GPF(13), "LCD power");
gpio_request(S3C64XX_GPF(15), "LCD power");

這是指取用 GPN pin 5 跟 GPF pin 13, pin 15 操作 LCD power

~linux/Documentation/gpio.txt 可以用 /sys/class/gpio/ 底下針對 gpiochip (controller) 或是 gpioXX (pin) 進行 user driver 的控制 in/out, high/low, trigger mode /sys/class/gpio/gpiochip0/ 中的 label 可以對應到 datasheet 的 controller 名稱,參考 arch/arm/mach-s3c64xx/include/mach/gpio.h 可以找到 S3C64XX_GPA, S3C64XX_GPB 等 gpio controller

S3C64XX_GPIO_A_NR (8) 定義 Bank A 有 8 根 gpio 腳位,依此類推對於 /sys/class/gpio/gpioXX/value read/write 就可以讀取 gpio 腳位high/low 或設定 high/low


2009年6月18日 星期四

Ubuntu 轉換AVCHD mts 檔為 DVD Video

基本上我是照 Acmelab's BlogHOWTO: Convert and write AVCHD (.mts) to DVD with Linux 轉換跟 jw 借的 Sony Full-HD HDR-SR11 硬碟攝影機錄下的 ssh 抓周影片,另外也要感謝 lkchu 全家的參與,讓抓周更熱鬧 :P

在 Acmelab 的步驟中,原本是轉 Canon, Panasonic 兩家的 mts 檔,但我試 Sony 錄的也可以成功轉換,也發現其實 mts 檔就是錄 H.264 (mpeg-4 AVC) 的影像格式,主要就只有五個步驟:

一、安裝 mplayer svn trunk
svn checkout svn://svn.mplayerhq.hu/mplayer/trunk mplayer
cd mplayer
./configure
make && make install

二、安裝 mencoder(在 mplayer trunk 內)、dvdauthor、growisofs(在 dvd+rw-tools 內)

三、轉換的指令列
>mencoder -oac copy -ovc lavc -of mpeg -mpegopts format=dvd -vf scale=720:576,harddup -lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=9800:vbitrate=5000:keyint=15:aspect=16/9:threads=4 input-file.mts -ofps 50 -fps 50 -o output_file.mpg

其中 input-file.mts 就是 DV 裡的 mts 檔,output_file.mpg 就是要輸出的 mpeg2 (因為原文是要轉 DVD-Video format) 檔案 DVD-Video 的 mpeg stream 限制

四、轉 mpeg2 為 dvd format
dvdauthor -o dvd -x dvd.xml
測試用:mplayer dvd:// -dvd-device ./dvd

五、燒錄 DVD 格式檔到光碟上
growisofs -dvd-compat -Z /dev/dvdrw -dvd-video ./dvd/

基本上,如果你以上步驟都正常,就比我幸運,不用再往下看了 :P

我另外遇到的問題是在 build mplayer 時,link 不到 libz? 於是另外安裝了 Ubuntu 的 libz package:

sudo apt-get install libz-dev
(或是 sudo apt-get remove zlib1g-dev)

然後在 configure mplayer 時要 link 到 libz
./configure --extra-ldflags=-lz

再 make mplayer 就可以把 mencoder 編譯出來了。
測試時是將檔案轉為 mpeg2 格式,就可以在 Windows 播出來了 :D
改的轉換參數隻有把 PAL 的 720x576 換成 NTSC 的 720x480

>mencoder -oac copy -ovc lavc -of mpeg -mpegopts format=dvd -vf scale=720:480,harddup -lavcopts vcodec=mpeg2video:vrc_buf_size=1835:vrc_maxrate=9800:vbitrate=5000:keyint=15:aspect=16/9:threads=4 input-file.mts -ofps 50 -fps 50 -o output_file.mpg

Cheers! Salute to ffmpeg! and AcmeLab :P

相關文章:

2008年2月18日 星期一

Linux 對電子產業的影響

自從 Google 公布開放手機聯盟Android 平台
以來,媒體的焦點都放在這是 Google 介入手機
產業。

另一個重要但被媒體忽略的影響,就是底層作業
系統採用 Linux 作業系統。也由於 Linux 開放源
碼的特性,對於一向採用封閉源碼的手機產業,
開始由程式碼的版權影響手機產業的發展。

開放源碼對程式設計的便利性,可以讓程式設計
師站在大量的開放源碼函式庫、現成應用軟體上
,也使得學習曲線立即趨緩,甚至可能在未完全
瞭解所應用軟體、函式庫時,就直接應用。

而對硬體開發的影響,可以參考電子工程專輯的
分析文章
,是否以後低階程式碼,如驅動程式,
Board Support Package 等硬體相依軟體,都將一
一開放,讓更多人可以直接檢視源碼?這勢必降
低進入偏硬體端的程式設計門檻,加速產業的開
發速度。

同樣參與開放手機聯盟的 Wind River,電子工程
專輯也報導 Wind River 找了協力廠商 ACCESS
進入 Linux 手機市場

以及前陣子眾多併購案中,Nokia 收購 Trolltech
並保持開放源碼授權的舉動,也是走向開放源碼
的一步棋。

手機市場走向開放原始碼大勢底定。

2008/05/21 update

電子工程專輯一篇為何嵌入式 Linux 令人又愛
又恨?
也點出了提供 Linux 解決方案的廠商為
了打廣告,不停地利用 Linux 的多資源與難以
維護等特性作廣告文宣,這種白熱化的廣告戰
也可以印證 Linux 對電子產業影響力的與日俱
增。

2008年2月16日 星期六

linux kernel vmsplice bugs to local root exploit

CVE-2008-0009
CVE-2008-0010
CVE-2008-0600

許久沒看 kernel trap 新聞,結果一翻開就是
bull's eye ,linux kernel local root exploit @@

0009 是 fs/splice.c 中 vmsplice_to_user() 並
未檢查 user space pointer 指向的資料,使用
者可能被用來存取 kernel space 的任意記憶體
位置。

0010 是 位於 fs/splice.c 的
copy_from_user_mmap_sem() 未檢查 user
space pointer 指向的資料,使用者程式可能
被用來取得 root 權限。

0600 是 vmsplice_to_pipe() 未檢查 user space
pointer 指向的資料,使用者程式可能被用
來取得 root 權限。