|
From:http://cid-b6be8a6f2ed519d0.spaces./blog/cns!B6BE8A6F2ED519D0!114.entry
//******************************************************* //* 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) 宏將物理地址轉(zhuǎn)換為了虛擬地址,然后再對這些虛擬地址進行讀寫操作。
------------------------------------------------------------------------
當應(yīng)用程序?qū)υO(shè)備文件進行ioctl操作時候會調(diào)用它們。對于fb_get_fix(),應(yīng)用程序傳入的是fb_fix_screeninfo結(jié)構(gòu),在函數(shù)中對其成員變量賦值,主要是smem_start(緩沖區(qū)起始地址)和smem_len(緩沖區(qū)長度),最終返回給應(yīng)用程序。 在/kernel/drivers/video/s3c2410fb.c 文件中的s3c2410fb_map_video_memory 函數(shù)中:
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) 這里首先計算出需要視頻緩沖區(qū)的大小(LCD屏的寬度 * LCD屏的高度 * 每像素的位數(shù) / 每字節(jié)的位數(shù)) 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 視頻緩沖區(qū)大小 (16K) | | | | -------|---------------|------- | ... | |---------------| //******************************************************* //* 2007.6.19 //******************************************************* 在/kernel/drivers/video/s3c2410fb.c 文件中的s3c2410fb_activate_var 函數(shù)中: unsigned long VideoPhysicalTemp = fbi->screen_dma; 這里已經(jīng)得到了framebuffer 在內(nèi)存中的起始地址為VideoPhysicalTemp,地址數(shù)據(jù)位為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 首先需要禁止視頻輸出才能進行寄存器的設(shè)置,然后對LCDCON2,LCDCON3 進行設(shè)置,主要是增加LINEVAL 和HOZVAL 這兩個顯示尺寸的參數(shù)。LCDCON4,LCDCON5 按原來配置設(shè)置。
LCDBANK[29:21] 為系統(tǒng)內(nèi)存中視頻緩沖區(qū)在系統(tǒng)存儲器內(nèi)的段地址的A[30:22] LCDBASEU[20:0] 為LCD framebuffer 的起始地址的A[21:1] LCDBASEL[20:0] 為LCD framebuffer 的結(jié)束地址的A[21:1] OFFSIZE[21:11] 為某一行的第一個半字與前一行最后一個半字之間的距離(單位:半字數(shù),即2個字節(jié)) PAGEWIDTH[10:0] 為顯示存儲區(qū)的可見幀寬度(單位:半字數(shù),即2個字節(jié)) 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 在內(nèi)存中的起始地址VideoPhysicalTemp,加上framebuffer 的大?。↙CD屏的寬度 * LCD屏的高度 * 每像素的位數(shù) / 每字節(jié)的位數(shù)),得到framebuffer 在內(nèi)存中的結(jié)束地址,然后右移1位。
new_regs.lcdsaddr3 = LCDADDR_OFFSET(0) | (LCDADDR_PAGE(var->xres));
這里PAGEWIDTH 的計算方法為:LCD屏的寬度*每像素的位數(shù)/16位 (半字)。
問題:以上這些操作是否已經(jīng)對DMA 控制器進行了設(shè)置? 我認為這里將framebuffer 在內(nèi)存中的起始地址為VideoPhysicalTemp 變換后載入LCDADDR1,LCDADDR2,LCDADDR3 中就已經(jīng)完成了對LCDCDMA 控制器的源數(shù)據(jù)的基地址設(shè)置,當打開LCDCON1 |= LCD1_ENVID; 后就可以由LCDCDMA 控制器自動從framebuffer 中傳數(shù)據(jù)到LCD 屏幕了。 ------------------------------------------------------------------------ 在/kernel/drivers/video/s3c2410fb.c 文件中的xxx_stn_info 結(jié)構(gòu)體初始化中: lcdcon5 : LCD5_FRM565 | LCD5_INVVLINE | LCD5_INVVFRAME | LCD5_HWSWP | LCD5_PWREN, INVVCLK , INVLINE , INVFRAME , INVVD :通過前面的時序圖,我們知道,CPU的LCD控制器輸出的時序默認是正脈沖,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均為負脈沖,因此 INVLINE 和 INVFRAME 必須設(shè)為“1 ”,即選擇反相輸出。 INVVDEN , INVPWREN , INVLEND 的功能同前面的類似。
PWREN 為LCD電源使能控制。在CPU LCD控制器的輸出信號中,有一個電源使能管腳LCD_PWREN,用來做為LCD屏電源的開關(guān)信號。
其中LCD5_HWSWP 一項,設(shè)置了LCD從內(nèi)存中顯示數(shù)據(jù)時,經(jīng)過了半字交換。
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 ... 例如:內(nèi)存地址的數(shù)據(jù)為:0x11223344 (32位)
系統(tǒng)存儲器采用Big-Endian(大端模式)存儲格式,地址數(shù)據(jù)格式如下: D[31:16] D[15:0] 00 01 02 03 00H 0x11 0x22 0x33 0x44 (0x1122) (0x3344) 04H ... 08H ... 則首先顯示0x3344 的數(shù)據(jù)到第一個像素,然后再顯示0x1122 到第二個像素。
系統(tǒng)存儲器采用Little-Endian(小端模式)存儲格式,地址數(shù)據(jù)格式如下:
D[31:16] D[15:0] 03 02 01 00 00H 0x11 0x22 0x33 0x44 (0x1122) (0x3344) 04H ... 08H ... 則首先顯示0x3344 的數(shù)據(jù)到第一個像素,然后再顯示0x1122 到第二個像素。
//*******************************************************
//* 2007.6.20 //******************************************************* 在/kernel/arch/arm/mm/consistent.c 文件中的consistent_alloc 函數(shù)中: 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); ... } 這里調(diào)用該函數(shù)來分配一段內(nèi)存空間有兩個返回值,一個返回值返回給了ret 指針,另一個返回值返回給了dma_handle 指針。virt_to_bus 和virt_to_phys 函數(shù)的調(diào)用可以參考下面的分析。經(jīng)過分析這兩個函數(shù)作用一樣,都是將virt 這個虛擬地址轉(zhuǎn)換為物理地址。所以返回給指針dma_handle 的是所分配內(nèi)存的起始地址(物理地址)。__ioremap 函數(shù)的調(diào)用也可以參考下面的說明,該函數(shù)也返回所分配內(nèi)存的起始地址(虛擬地址),不過是經(jīng)過I/O 內(nèi)存映射的,把物理地址轉(zhuǎn)換為了虛擬地址。
這樣一來就很清楚了,返回的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 函數(shù))。 ------------------------------------------------------------------------ 在/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) 在該文件中包含了所有驅(qū)動LCD 的函數(shù)。在fb_read 和fb_write 這兩個函數(shù)中,都對framebuffer 進行了操作。
fb_read 函數(shù)中: char *base_addr; base_addr = info->disp->screen_base; count -= copy_to_user(buf, base_addr+p, count); fb_write 函數(shù)中:
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 視頻緩沖區(qū)大小 (16K) | | | | -------|---------------|------- | ... | |---------------| 其中display->screen_base 結(jié)構(gòu)在/kernel/include/video/fbcon.h 文件中定義。
得出結(jié)論,在分配framebuffer 時一共返回兩個指針,雖然是同一塊內(nèi)存空間,但一個返回的是實際的物理地址,另一個返回的是經(jīng)過地址轉(zhuǎn)換的虛擬地址。在設(shè)置LCD 控制器中framebuffer 起始地址寄存器時,用的是所分配內(nèi)存的物理地址;而當要對framebuffer 進行讀寫操作時,用的是同一塊內(nèi)存的物理地址所轉(zhuǎn)換后的虛擬地址。由此可以知道,內(nèi)核在對每個I/O 地址進行讀寫操作時用的都是經(jīng)過轉(zhuǎn)換的虛擬地址。
------------------------------------------------------------------------ ------------------------------------------------------------------------ 在/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。這樣一來,以上對物理地址和虛擬地址之間轉(zhuǎn)換的宏定義就很好理解了。 虛擬地址轉(zhuǎn)為物理地址宏:__virt_to_phys(x) = (x) - (PAGE_OFFSET - PHYS_OFFSET) = (x) - PAGE_OFFSET + PHYS_OFFSET 物理地址轉(zhuǎn)為虛擬地址宏:__phys_to_virt(x) = (x) + (PAGE_OFFSET - PHYS_OFFSET) = (x) + PAGE_OFFSET - PHYS_OFFSET 內(nèi)核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),可以很方便的將其轉(zhuǎn)化為物理內(nèi)存地址,同時內(nèi)核也提供了virt_to_phys() 函數(shù)將內(nèi)核虛擬空間中的物理影射區(qū)地址轉(zhuǎn)化為物理地址。
/* * 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) 這兩個函數(shù)調(diào)用的都是__virt_to_phys(x) 即((x) - PAGE_OFFSET + PHYS_OFFSET)。所以這兩個調(diào)用都是將虛擬地址轉(zhuǎn)換為了物理地址。 ------------------------------------------------------------------------ 在/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 函數(shù)的作用是將physical address以及bus address映射為kernel的
virtual adrress。 ioremap 的作用是把I/O 內(nèi)存地址(物理地址)映射到虛擬地址空間,使用之前需要分配I/O 內(nèi)存區(qū)域。但是,ioremap 函數(shù)的內(nèi)部實現(xiàn)并不是簡單的((IO的物理地址)-0x10000000 +0xE4000000)。所以,得到的虛擬地址可能是不同的。 因為linux 用的是頁面映射機制,CPU 不能按物理地址來訪問存儲空間,而必須使用虛擬地址,所以必須反向的從物理地址出發(fā)找到一片虛存空間并建立起映射。 //******************************************************* //* 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 }; 這個結(jié)構(gòu)中定義了對LCD 所分配的framebuffer 進行讀寫操作,以及一些內(nèi)存映射之類的函數(shù),這些源函數(shù)都在該文件中。
/** * 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 設(shè)備進行注冊時調(diào)用到的注冊函數(shù),該函數(shù)在/kernel/drivers/video/s3c2410fb.c 文件的s3c2410fb_init 函數(shù)里的最后部分被調(diào)用。
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); 在注冊函數(shù)的最后部分,將前面的fb_fops 結(jié)構(gòu)里的各個驅(qū)動函數(shù)的入口點傳入到devfs_register()設(shè)備注冊函數(shù)中。
//******************************************************* //* 2007.6.26 //******************************************************* 上面這個devfs_register 函數(shù)(原型在/kernel/fs/devfs/base.c 文件中)執(zhí)行前,在fbmem_init 函數(shù)中調(diào)用了函數(shù): 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 函數(shù)會在設(shè)備文件系統(tǒng)中創(chuàng)建一個名為fb 的目錄,并返回一個帶有devfs 設(shè)備文件系統(tǒng)目錄結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)變量devfs_handle。然后把這個數(shù)據(jù)結(jié)構(gòu)作為下一步調(diào)用devfs_register 函數(shù)時的參數(shù),該參數(shù)在調(diào)用設(shè)備文件系統(tǒng)注冊清除函數(shù)devfs_unregister 時也要作為參數(shù)傳入。 /** * 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) 函數(shù)devfs_register 是設(shè)備文件系統(tǒng)的主冊函數(shù),會在剛才創(chuàng)建的目錄下再創(chuàng)建一個名為name 的設(shè)備文件節(jié)點。返回的devfs_handle_t 數(shù)據(jù)結(jié)構(gòu)變量會在調(diào)用設(shè)備文件系統(tǒng)注冊清除函數(shù)devfs_unregister 時作為參數(shù)傳入。
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); 調(diào)用該函數(shù)后,會在剛才創(chuàng)建的fb 目錄下再創(chuàng)建一個名為0 (name_buf)的設(shè)備文件節(jié)點,并賦予相應(yīng)的權(quán)限,以及主次設(shè)備號和file_operations 結(jié)構(gòu)的函數(shù)入口。 這樣一來,Linux 設(shè)備文件的創(chuàng)建,刪除和目錄層次等都由各設(shè)備驅(qū)動程序管理,再也不用手工創(chuàng)建設(shè)備文件節(jié)點了,再也不需要mknod 時查找對應(yīng)的主設(shè)備號了,也不用依靠復(fù)雜的腳本來管理設(shè)備文件了。 |
|
|