LCD驅動程序分析---基於framebuffer

LCD驅動程序分析---基於framebuffer
2009-01-03 13:54
//*******************************************************
//* 2007.6.18
//*******************************************************
在/kernel/include/asm-arm/arch-s3c2410/bitfield.h 文件中:
#ifndef __ASSEMBLY__
#define UData(Data) ((unsigned long) (Data))
#else
#define UData(Data) (Data)
#endif
例:UData(5); = 5
/*
* MACRO: Fld
*
* Purpose
*    The macro "Fld" encodes a bit field, given its size and its shift value
*    with respect to bit 0.
*
* Note
*    A more intuitive way to encode bit fields would have been to use their
*    mask. However, extracting size and shift value information from a bit
*    field''''s mask is cumbersome and might break the assembler (255-character
*    line-size limit).
*
* Input
*    Size      Size of the bit field, in number of bits.
*    Shft      Shift value of the bit field with respect to bit 0.
*
* Output
*    Fld       Encoded bit field.
*/
#define Fld(Size, Shft) (((Size) << 16) + (Shft))
例:Fld(2,5); = 0x20005
/*
* MACROS: FSize, FShft, FMsk, FAlnMsk, F1stBit
*
* Purpose
*    The macros "FSize", "FShft", "FMsk", "FAlnMsk", and "F1stBit" return
*    the size, shift value, mask, aligned mask, and first bit of a
*    bit field.
*
* Input
*    Field     Encoded bit field (using the macro "Fld").
*
* Output
*    FSize     Size of the bit field, in number of bits.
*    FShft     Shift value of the bit field with respect to bit 0.
*    FMsk      Mask for the bit field.
*    FAlnMsk   Mask for the bit field, aligned on bit 0.
*    F1stBit   First bit of the bit field.
*/
#define FSize(Field) ((Field) >> 16)
例:FSize(0x20005); = 2
#define FShft(Field) ((Field) & 0x0000FFFF)
例:FShft(0x20005); = 5
/*
* MACRO: FInsrt
*
* Purpose
*    The macro "FInsrt" inserts a value into a bit field by shifting the
*    former appropriately.
*
* Input
*    Value     Bit-field value.
*    Field     Encoded bit field (using the macro "Fld").
*
* Output
*    FInsrt    Bit-field value positioned appropriately.
*/
#define FInsrt(Value, Field) /
                (UData (Value) << FShft (Field))
例:FInsrt(0x3, 0x20005); = 0x3 << 0x0005 = 0x60
------------------------------------------------------------------------
在/kernel/include/asm-arm/arch-s3c2410/hardware.h 文件中:
/*
* S3C2410 internal I/O mappings
*
* We have the following mapping:
*   phys   virt
*   48000000 e8000000
*/
#define VIO_BASE   0xe8000000 /* virtual start of IO space */
#define PIO_START   0x48000000 /* physical start of IO space */
#define io_p2v(x) ((x) | 0xa0000000)
#define io_v2p(x) ((x) & ~0xa0000000)
# define __REG(x) io_p2v(x)
# define __PREG(x) io_v2p(x)
    這裏,在實際的寄存器操作中,都用__REG(x) 宏將物理地址轉換爲了虛擬地址,然後再對這些虛擬地址進行讀寫操作。
------------------------------------------------------------------------
    當應用程序對設備文件進行ioctl操作時候會調用它們。對於fb_get_fix(),應用程序傳入的是fb_fix_screeninfo結構,在函 數中對其成員變量賦值,主要是smem_start(緩衝區起始地址)和smem_len(緩衝區長度),最終返回給應用程序。
在/kernel/drivers/video/s3c2410fb.c 文件中的s3c2410fb_map_video_memory 函數中:
fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres *
      fbi->max_bpp / 8;
fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
            &fbi->map_dma);
if (fbi->map_cpu)
{
fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE;
fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
fbi->fb.fix.smem_start = fbi->screen_dma;
}

在/kernel/include/asm-arm/proc-armo/page.h 文件中:
/* PAGE_SHIFT determines the page size. This is configurable. */
#if defined(CONFIG_PAGESIZE_16)
#define PAGE_SHIFT 14   /* 16K */
#else   /* default */
#define PAGE_SHIFT 15   /* 32K */
#endif
在/kernel/include/asm-arm/page.h 文件中:
#define PAGE_SIZE       (1UL << PAGE_SHIFT)
#define PAGE_MASK       (~(PAGE_SIZE-1))
/* to align the pointer to the (next) page boundary */
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
在/kernel/arch/arm/mm/consistent.c 文件中:
/*
* This allocates one page of cache-coherent memory space and returns
* both the virtual and a "dma" address to that space. It is not clear
* whether this could be called from an interrupt context or not. For
* now, we expressly forbid it, especially as some of the stuff we do
* here is not interrupt context safe.
*
* Note that this does *not* zero the allocated area!
*/
void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)

    這裏首先計算出需要視頻緩衝區的大小(LCD屏的寬度 * LCD屏的高度 * 每像素的位數 / 每字節的位數)
fbi->fb.fix.smem_len = 240*320*16/8 = 0x25800 =150K(9.375個PAGE)
PAGE_SHIFT = 14
PAGE_SIZE = 1<<14 = 0x4000 = 16K (1個PAGE)
PAGE_MASK = 0xFFFFC000
fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE) = PAGE_ALIGN(150K + 16K) = PAGE_ALIGN(166K)
= (166K + 16K - 1) & 0xFFFFC000 = 0x2D7FF & 0xFFFFC000 = 0x2C000 =176K
consistent_alloc(GFP_KERNEL, 176K, &fbi->map_dma);
最後得到:
      framebuffer(物理地址)
|---------------|
|      ... |
-------|---------------| <-- fbi->map_dma
|      16K |
分配了 |---------------| <-- fbi->screen_dma = fbi->fb.fix.smem_start
176K |   |
共11個 |   | 160K = 10個PAGE
PAGE |      160K | 可以容下所需的150K 視頻緩衝區大小
(16K) |   |
|   |
-------|---------------|-------
|      ... |
|---------------|

//*******************************************************
//* 2007.6.19
//*******************************************************
在/kernel/drivers/video/s3c2410fb.c 文件中的s3c2410fb_activate_var 函數中:
unsigned long VideoPhysicalTemp = fbi->screen_dma;
    這裏已經得到了framebuffer 在內存中的起始地址爲VideoPhysicalTemp,地址數據位爲A[30:0]。
new_regs.lcdcon1 = fbi->reg.lcdcon1 & ~LCD1_ENVID;
new_regs.lcdcon2 = (fbi->reg.lcdcon2 & ~LCD2_LINEVAL_MSK)
       | LCD2_LINEVAL(var->yres - 1);
/* TFT LCD only ! */
new_regs.lcdcon3 = (fbi->reg.lcdcon3 & ~LCD3_HOZVAL_MSK)
       | LCD3_HOZVAL(var->xres - 1);
new_regs.lcdcon4 = fbi->reg.lcdcon4;
new_regs.lcdcon5 = fbi->reg.lcdcon5;
   LCDCON1 首先需要禁止視頻輸出才能進行寄存器的設置,然後對LCDCON2,LCDCON3 進行設置,主要是增加LINEVAL 和HOZVAL 這兩個顯示尺寸的參數。LCDCON4,LCDCON5 按原來配置設置。

LCDBANK[29:21] 爲系統內存中視頻緩衝區在系統存儲器內的段地址的A[30:22]
LCDBASEU[20:0] 爲LCD framebuffer 的起始地址的A[21:1]
LCDBASEL[20:0] 爲LCD framebuffer 的結束地址的A[21:1]
OFFSIZE[21:11] 爲某一行的第一個半字與前一行最後一個半字之間的距離(單位:半字數,即2個字節)
PAGEWIDTH[10:0] 爲顯示存儲區的可見幀寬度(單位:半字數,即2個字節)

new_regs.lcdsaddr1 =
   LCDADDR_BANK(((unsigned long)VideoPhysicalTemp >> 22))
   | LCDADDR_BASEU(((unsigned long)VideoPhysicalTemp >> 1));
new_regs.lcdsaddr2 = LCDADDR_BASEL(
   ((unsigned long)VideoPhysicalTemp + (var->xres * 2 * (var>yres))) >> 1);
    這裏LCDADDR_BASEL 的計算方法爲用framebuffer 在內存中的起始地址VideoPhysicalTemp,加上framebuffer 的大小(LCD屏的寬度 * LCD屏的高度 * 每像素的位數 / 每字節的位數),得到framebuffer 在內存中的結束地址,然後右移1位。
new_regs.lcdsaddr3 = LCDADDR_OFFSET(0) | (LCDADDR_PAGE(var->xres));
    這裏PAGEWIDTH 的計算方法爲:LCD屏的寬度*每像素的位數/16位 (半字)。

問題:以上這些操作是否已經對DMA 控制器進行了設置?
    我認爲這裏將framebuffer 在內存中的起始地址爲VideoPhysicalTemp 變換後載入LCDADDR1,LCDADDR2,LCDADDR3 中就已經完成了對LCDCDMA 控制器的源數據的基地址設置,當打開LCDCON1 |= LCD1_ENVID; 後就可以由LCDCDMA 控制器自動從framebuffer 中傳數據到LCD 屏幕了。

------------------------------------------------------------------------
在/kernel/drivers/video/s3c2410fb.c 文件中的xxx_stn_info 結構體初始化中:
lcdcon5 : LCD5_FRM565 | LCD5_INVVLINE | LCD5_INVVFRAME | LCD5_HWSWP | LCD5_PWREN,
    INVVCLK , INVLINE , INVFRAME , INVVD :通過前面的時序圖,我們知道,CPU的LCD控制器輸出的時序默認是正脈衝,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均爲負 脈衝,因此 INVLINE 和 INVFRAME 必須設爲“1 ”,即選擇反相輸出。 INVVDEN , INVPWREN , INVLEND 的功能同前面的類似。
    PWREN 爲LCD電源使能控制。在CPU LCD控制器的輸出信號中,有一個電源使能管腳LCD_PWREN,用來做爲LCD屏電源的開關信號。
    其中LCD5_HWSWP 一項,設置了LCD從內存中顯示數據時,經過了半字交換。
16BPP Display
(BSWP = 0, HWSWP = 0)
D[31:16] D[15:0]
000H P1 P2
004H P3 P4
008H P5 P6
...
(BSWP = 0, HWSWP = 1)
D[31:16] D[15:0]
000H P2 P1
004H P4 P3
008H P6 P5
...
像素顯示順序如下:
P1 P2 P3 P4 P5 ...
例如:內存地址的數據爲:0x11223344 (32位)
    系統存儲器採用Big-Endian(大端模式)存儲格式,地址數據格式如下:
D[31:16] D[15:0]
00   01   02   03
00H     0x11 0x22 0x33 0x44
        (0x1122) (0x3344)
04H ...
08H ...
則首先顯示0x3344 的數據到第一個像素,然後再顯示0x1122 到第二個像素。
    系統存儲器採用Little-Endian(小端模式)存儲格式,地址數據格式如下:
D[31:16] D[15:0]
03   02   01   00
00H     0x11 0x22 0x33 0x44
        (0x1122) (0x3344)
04H ...
08H ...
則首先顯示0x3344 的數據到第一個像素,然後再顯示0x1122 到第二個像素。
//*******************************************************
//* 2007.6.20
//*******************************************************
在/kernel/arch/arm/mm/consistent.c 文件中的consistent_alloc 函數中:
void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
{
...
virt = page_address(page);
*dma_handle = virt_to_bus(virt);
ret = __ioremap(virt_to_phys(virt), size, 0);
...
}
    這裏調用該函數來分配一段內存空間有兩個返回值,一個返回值返回給了ret 指針,另一個返回值返回給了dma_handle 指針。virt_to_bus 和virt_to_phys 函數的調用可以參考下面的分析。經過分析這兩個函數作用一樣,都是將virt 這個虛擬地址轉換爲物理地址。所以返回給指針dma_handle 的是所分配內存的起始地址(物理地址)。__ioremap 函數的調用也可以參考下面的說明,該函數也返回所分配內存的起始地址(虛擬地址),不過是經過I/O 內存映射的,把物理地址轉換爲了虛擬地址。
    這樣一來就很清楚了,返回的framebuffer 的物理地址給了指針dma_handle,也就是fbi->map_dma,到fbi->screen_dma,再到fbi-> fb.fix.smem_start,最後到了指針VideoPhysicalTemp,這樣寫入到LCDADDR1,LCDADDR2 寄存器中的framebuffer 的地址其實都是物理地址。
    而返回的framebuffer 的虛擬地址給了指針ret,也就是fbi->map_cpu,到fbi->screen_cpu,最後到了display-> screen_base(見/kernel/drivers/video/s3c2410fb.c 文件中的s3c2410fb_set_var 函數)。

------------------------------------------------------------------------
在/kernel/drivers/video/fbmem.c 文件中:
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
*
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/
int
register_framebuffer(struct fb_info *fb_info)
/**
* unregister_framebuffer - releases a frame buffer device
* @fb_info: frame buffer info structure
*
* Unregisters a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/
int
unregister_framebuffer(struct fb_info *fb_info)
static int
fb_open(struct inode *inode, struct file *file)
static int
fb_release(struct inode *inode, struct file *file)
static ssize_t
fb_read(struct file *file, char *buf, size_t count, loff_t *ppos)
static ssize_t
fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
static int
fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
    在該文件中包含了所有驅動LCD 的函數。在fb_read 和fb_write 這兩個函數中,都對framebuffer 進行了操作。
fb_read 函數中:
char *base_addr;
base_addr = info->disp->screen_base;
count -= copy_to_user(buf, base_addr+p, count);
fb_write 函數中:
char *base_addr;
base_addr = info->disp->screen_base;
count -= copy_from_user(base_addr+p, buf, count);

所讀寫的framebuffer 的基地址就是disp->screen_base,也就是fbi->screen_cpu 所指的framebuffer 的虛擬地址。
從而得到:
      framebuffer(虛擬地址)
|---------------|
|      ... |
-------|---------------| <-- fbi->map_cpu
|      16K |
分配了 |---------------| <-- fbi->screen_cpu = display->screen_base
176K |   |
共11個 |   | 160K = 10個PAGE
PAGE |      160K | 可以容下所需的150K 視頻緩衝區大小
(16K) |   |
|   |
-------|---------------|-------
|      ... |
|---------------|
其中display->screen_base 結構在/kernel/include/video/fbcon.h 文件中定義。
    得出結論,在分配framebuffer 時一共返回兩個指針,雖然是同一塊內存空間,但一個返回的是實際的物理地址,另一個返回的是經過地址轉換的虛擬地址。在設置LCD 控制器中framebuffer 起始地址寄存器時,用的是所分配內存的物理地址;而當要對framebuffer 進行讀寫操作時,用的是同一塊內存的物理地址所轉換後的虛擬地址。由此可以知道,內核在對每個I/O 地址進行讀寫操作時用的都是經過轉換的虛擬地址。

------------------------------------------------------------------------
------------------------------------------------------------------------
在/kernel/include/asm-arm/arch-s3c2410/memory.h 文件中:
/*
* Page offset: 3GB
*/
#define PAGE_OFFSET (0xc0000000UL)
#define PHYS_OFFSET (0x30000000UL)
/*
* We take advantage of the fact that physical and virtual address can be the
* saem. Thu NUMA code is handling the large holes that might exist between
* all memory banks.
*/
#define __virt_to_phys__is_a_macro
#define __phys_to_virt__is_a_macro
#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)

    由此可見:                 起始點地址     PHYS_OFFSET      PAGE_OFFSET
            |   |
     |--0x30000000--|------間隔------|
PHYS_OFFSET(物理地址起始點): |--------------|...........................
         |
    |-----------0xc0000000----------|
PAGE_OFFSET(虛擬地址起始點): |-------------------------------|..........

    物理地址與虛擬地址的間隔爲:PAGE_OFFSET - PHYS_OFFSET。這樣一來,以上對物理地址和虛擬地址之間轉換的宏定義就很好理解了。
虛擬地址轉爲物理地址宏:__virt_to_phys(x)
= (x) - (PAGE_OFFSET - PHYS_OFFSET) = (x) - PAGE_OFFSET + PHYS_OFFSET
物理地址轉爲虛擬地址宏:__phys_to_virt(x)
= (x) + (PAGE_OFFSET - PHYS_OFFSET) = (x) + PAGE_OFFSET - PHYS_OFFSET
    內核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),可以很方便的將其轉化爲物理內存地址,同時內核也提供了virt_to_phys() 函數將內核虛擬空間中的物理影射區地址轉化爲物理地址。

/*
* Virtual view <-> DMA view memory address translations
* virt_to_bus: Used to translate the virtual address to an
*              address suitable to be passed to set_dma_addr
* bus_to_virt: Used to convert an address for DMA operations
*              to an address that the kernel can use.
*/
#define __virt_to_bus__is_a_macro
#define __bus_to_virt__is_a_macro
#define __virt_to_bus(x) __virt_to_phys(x)
#define __bus_to_virt(x) __phys_to_virt(x)

    這裏注意:__virt_to_bus(x) 就等於__virt_to_phys(x)
------------------------------------------------------------------------
在/kernel/include/asm-arm/memory.h 文件中:
/*
* These are *only* valid on the kernel direct mapped RAM memory.
*/
static inline unsigned long virt_to_phys(volatile void *x)
{
return __virt_to_phys((unsigned long)(x));
}
/*
* Virtual <-> DMA view memory address translations
* Again, these are *only* valid on the kernel direct mapped RAM
* memory.
*/
#define virt_to_bus(x)   (__virt_to_bus((unsigned long)(x)))

    由上面的分析可知:virt_to_bus(x) 和virt_to_phys(volatile void *x) 這兩個函數調用的都是__virt_to_phys(x) 即((x) - PAGE_OFFSET + PHYS_OFFSET)。所以這兩個調用都是將虛擬地址轉換爲了物理地址。

------------------------------------------------------------------------
在/kernel/arch/arm/mm/ioremap.c 文件中:
/*
* Remap an arbitrary physical address space into the kernel virtual
* address space. Needed when the kernel wants to access high addresses
* directly.
*
* NOTE! We need to allow non-page-aligned mappings too: we will obviously
* have to convert them into an offset in a page-aligned mapping, but the
* caller shouldn''''t need to know that small detail.
*
* ''''flags'''' are the extra L_PTE_ flags that you want to specify for this
* mapping. See include/asm-arm/proc-armv/pgtable.h for more information.
*/
void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
    ioremap 函數的作用是將physical address以及bus address映射爲kernel的
virtual adrress。
    ioremap 的作用是把I/O 內存地址(物理地址)映射到虛擬地址空間,使用之前需要分配I/O 內存區域。但是,ioremap 函數的內部實現並不是簡單的((IO的物理地址)-0x10000000 +0xE4000000)。所以,得到的虛擬地址可能是不同的。
    因爲linux 用的是頁面映射機制,CPU 不能按物理地址來訪問存儲空間,而必須使用虛擬地址,所以必須反向的從物理地址出發找到一片虛存空間並建立起映射。

//*******************************************************
//* 2007.6.22
//*******************************************************
在/kernel/drivers/video/fbmem.c 文件中:
static struct file_operations fb_fops = {
owner:   THIS_MODULE,
read:   fb_read,
write:   fb_write,
ioctl:   fb_ioctl,
mmap:   fb_mmap,
open:   fb_open,
release: fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
get_unmapped_area: get_fb_unmapped_area,
#endif
};
這個結構中定義了對LCD 所分配的framebuffer 進行讀寫操作,以及一些內存映射之類的函數,這些源函數都在該文件中。

/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
*
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/
int
register_framebuffer(struct fb_info *fb_info)
這個是對LCD 的framebuffer 設備進行註冊時調用到的註冊函數,該函數在/kernel/drivers/video/s3c2410fb.c 文件的s3c2410fb_init 函數裏的最後部分被調用。
fb_info->devfs_handle =
     devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT,
       FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO,
       &fb_fops, NULL);
在註冊函數的最後部分,將前面的fb_fops 結構裏的各個驅動函數的入口點傳入到devfs_register()設備註冊函數中。

//*******************************************************
//* 2007.6.26
//*******************************************************
    上面這個devfs_register 函數(原型在/kernel/fs/devfs/base.c 文件中)執行前,在fbmem_init 函數中調用了函數:
devfs_handle = devfs_mk_dir (NULL, "fb", NULL);

在/kernel/fs/devfs/base.c 文件中:
/**
* devfs_mk_dir - Create a directory in the devfs namespace.
* @dir: The handle to the parent devfs directory entry. If this is %NULL the
*   new name is relative to the root of the devfs.
* @name: The name of the entry.
* @info: An arbitrary pointer which will be associated with the entry.
*
* Use of this function is optional. The devfs_register() function
* will automatically create intermediate directories as needed. This function
* is provided for efficiency reasons, as it provides a handle to a directory.
* Returns a handle which may later be used in a call to devfs_unregister().
* On failure %NULL is returned.
*/
devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)

    這個devfs_mk_dir 函數會在設備文件系統中創建一個名爲fb 的目錄,並返回一個帶有devfs 設備文件系統目錄結構的數據結構變量devfs_handle。然後把這個數據結構作爲下一步調用devfs_register 函數時的參數,該參數在調用設備文件系統註冊清除函數devfs_unregister 時也要作爲參數傳入。

/**
* devfs_register - Register a device entry.
* @dir: The handle to the parent devfs directory entry. If this is %NULL the
*   new name is relative to the root of the devfs.
* @name: The name of the entry.
* @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
* @major: The major number. Not needed for regular files.
* @minor: The minor number. Not needed for regular files.
* @mode: The default file mode.
* @ops: The &file_operations or &block_device_operations structure.
*   This must not be externally deallocated.
* @info: An arbitrary pointer which will be written to the @private_data
*   field of the &file structure passed to the device driver. You can set
*   this to whatever you like, and change it once the file is opened (the next
*   file opened will not see this change).
*
* Returns a handle which may later be used in a call to devfs_unregister().
* On failure %NULL is returned.
*/
devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
          unsigned int flags,
          unsigned int major, unsigned int minor,
          umode_t mode, void *ops, void *info)
    函數devfs_register 是設備文件系統的主冊函數,會在剛纔創建的目錄下再創建一個名爲name 的設備文件節點。返回的devfs_handle_t 數據結構變量會在調用設備文件系統註冊清除函數devfs_unregister 時作爲參數傳入。

fb_info->devfs_handle =
     devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT,
       FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO,
       &fb_fops, NULL);

    調用該函數後,會在剛纔創建的fb 目錄下再創建一個名爲0 (name_buf)的設備文件節點,並賦予相應的權限,以及主次設備號和file_operations 結構的函數入口。
    這樣一來,Linux 設備文件的創建,刪除和目錄層次等都由各設備驅動程序管理,再也不用手工創建設備文件節點了,再也不需要mknod 時查找對應的主設備號了,也不用依靠複雜的腳本來管理設備文件了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章