亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

Linux字符設(shè)備驅(qū)動(dòng)開發(fā)框架介紹

2023-04-15 15:05:08 來源:嵌入式攻城獅

1. 字符設(shè)備驅(qū)動(dòng)簡(jiǎn)介

字符設(shè)備是Linux驅(qū)動(dòng)中最基本的一類設(shè)備驅(qū)動(dòng),字符設(shè)備就是一個(gè)一個(gè)字節(jié),按照字節(jié)流進(jìn)行讀寫操作的設(shè)備,讀寫數(shù)據(jù)是分先后順序的。 比如常見的點(diǎn)燈、按鍵、IIC、SPI、LCD 等等都是字符設(shè)備,這些設(shè)備的驅(qū)動(dòng)就叫做字符設(shè)備驅(qū)動(dòng)。

Linux驅(qū)動(dòng)基本原理:Linux中一切皆為文件,驅(qū)動(dòng)加載成功后會(huì)在/dev目錄下生成一個(gè)相應(yīng)的文件,應(yīng)用程序通過對(duì)這個(gè)名為/dev/xxx的文件進(jìn)行相應(yīng)的操作即可實(shí)現(xiàn)對(duì)硬件的操作。


(資料圖)

比如LED驅(qū)動(dòng),會(huì)有/dev/led驅(qū)動(dòng)文件,應(yīng)用程序使用open函數(shù)來打開該文件; 若要點(diǎn)亮或關(guān)閉led,就使用write函數(shù)寫入開關(guān)值; 若要獲取led燈的狀態(tài),就用read函數(shù)從驅(qū)動(dòng)文件中讀取相應(yīng)的狀態(tài); 使用完成后使用close函數(shù)關(guān)閉該驅(qū)動(dòng)文件。

Linux軟件從上到下可分為4層結(jié)構(gòu),如下圖左示。 以控制LED為例,具體過程如下圖右示:

每個(gè)系統(tǒng)調(diào)用,在驅(qū)動(dòng)中都有與之對(duì)應(yīng)的驅(qū)動(dòng)函數(shù),內(nèi)核include/linux/fs.h文件中有個(gè)file_operations結(jié)構(gòu)體,就是Linux內(nèi)核驅(qū)動(dòng)操作函數(shù)集合:

struct file_operations {  struct module *owner;  loff_t (*llseek) (struct file *, loff_t, int);  ssize_t (*read) (struct file *, char __user *, size_t, loff_t*);  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);  ......  ......};

Linux驅(qū)動(dòng)運(yùn)行方式有以下兩種:

將驅(qū)動(dòng)編譯進(jìn)內(nèi)核中, 當(dāng)Linux內(nèi)核啟動(dòng)時(shí)就會(huì)自動(dòng)運(yùn)行驅(qū)動(dòng)程序?qū)Ⅱ?qū)動(dòng)編譯成模塊, 在內(nèi)核啟動(dòng)后使用insmod命令加載驅(qū)動(dòng)模塊

在驅(qū)動(dòng)開發(fā)階段一般都將其編譯為模塊,不需要編譯整個(gè)Linux代碼,方便調(diào)試驅(qū)動(dòng)程序。 當(dāng)驅(qū)動(dòng)開發(fā)完成后,根據(jù)實(shí)際需要,可以選擇是否將驅(qū)動(dòng)編譯進(jìn)Linux內(nèi)核中。

2. Linux設(shè)備號(hào)

2.1 設(shè)備號(hào)的組成

Linux中每個(gè)設(shè)備都有一個(gè)設(shè)備號(hào),由主設(shè)備號(hào)和次設(shè)備號(hào)兩部分組成:

主設(shè)備號(hào)表示某一個(gè)具體的驅(qū)動(dòng)次設(shè)備號(hào)表示使用這個(gè)驅(qū)動(dòng)的各個(gè)設(shè)備

Linux 提供了名為dev_t的數(shù)據(jù)類型表示設(shè)備號(hào),其本質(zhì)是32位的unsigned int數(shù)據(jù)類型,其中高12位為主設(shè)備號(hào),低20位為次設(shè)備號(hào),因此Linux中主設(shè)備號(hào)范圍為0~4095

在文件include/linux/kdev_t.h中提供了幾個(gè)關(guān)于設(shè)備號(hào)操作的宏定義:

#define MINORBITS 20#define MINORMASK ((1U << MINORBITS) - 1)#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
MINORBITS:表示次設(shè)備號(hào)位數(shù),一共20位MINORMASK:表示次設(shè)備號(hào)掩碼MAJOR:用于從dev_t中獲取主設(shè)備號(hào),將dev_t右移20位即可MINOR:用于從dev_t中獲取次設(shè)備號(hào),取dev_t的低20位的值即可MKDEV:用于將給定的主設(shè)備號(hào)和次設(shè)備號(hào)的值組合成dev_t類型的設(shè)備號(hào)

2.2 主設(shè)備號(hào)的分配

主設(shè)備號(hào)的分配包括靜態(tài)分配和動(dòng)態(tài)分配。 靜態(tài)分配需要手動(dòng)指定設(shè)備號(hào),并且要注意不能與已有的重復(fù),一些常用的設(shè)備號(hào)已經(jīng)被Linux內(nèi)核開發(fā)者給分配掉了,可使用cat /proc/devices命令查看當(dāng)前系統(tǒng)中所有已經(jīng)使用了的設(shè)備號(hào)。

動(dòng)態(tài)分配是在注冊(cè)字符設(shè)備之前先申請(qǐng)一個(gè)設(shè)備號(hào),系統(tǒng)會(huì)自動(dòng)分配一個(gè)沒有被使用的設(shè)備號(hào), 這樣就避免了沖突。 在卸載驅(qū)動(dòng)的時(shí)候釋放掉這個(gè)設(shè)備號(hào)即可。

設(shè)備號(hào)的申請(qǐng)函數(shù)

//設(shè)備號(hào)申請(qǐng)函數(shù)int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)// dev:保存申請(qǐng)到的設(shè)備號(hào)// baseminor:次設(shè)備號(hào)起始地址// count:要申請(qǐng)的設(shè)備號(hào)數(shù)量// name:設(shè)備名字

設(shè)備號(hào)的釋放函數(shù)

//設(shè)備號(hào)釋放函數(shù)void unregister_chrdev_region(dev_t from, unsigned count)// from:要釋放的設(shè)備號(hào)// count:表示從 from 開始,要釋放的設(shè)備號(hào)數(shù)量

3. 字符設(shè)備驅(qū)動(dòng)開發(fā)模板

3.1 加載與卸載

在編寫驅(qū)動(dòng)的時(shí)候需要注冊(cè)模塊加載和卸載這兩種函數(shù):

module_init(xxx_init);    //注冊(cè)模塊加載函數(shù)module_exit(xxx_exit);    //注冊(cè)模塊卸載函數(shù)

module_init():向內(nèi)核注冊(cè)一個(gè)模塊加載函數(shù),參數(shù)xxx_init就是需要注冊(cè)的具體函數(shù),當(dāng)使用insmod命令加載驅(qū)動(dòng)時(shí),xxx_init函數(shù)就會(huì)被調(diào)用

module_exit():向內(nèi)核注冊(cè)一個(gè)模塊卸載函數(shù),參數(shù)xxx_exit就是需要注冊(cè)的具體函數(shù),當(dāng)使用rmmod命令卸載驅(qū)動(dòng)時(shí),xxx_exit函數(shù)就會(huì)被調(diào)用

字符設(shè)備驅(qū)動(dòng)模塊加載和卸載模板如下所示:

/* 驅(qū)動(dòng)入口函數(shù) */staticint __init xxx_init(void){/*入口函數(shù)具體內(nèi)容*/  return0;}/* 驅(qū)動(dòng)出口函數(shù) */staticvoid __exit xxx_exit(void){/*出口函數(shù)具體內(nèi)容*/}/* 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */module_init(xxx_init);module_exit(xxx_exit);

驅(qū)動(dòng)編譯完成以后擴(kuò)展名為.ko,有兩種命令可以加載驅(qū)動(dòng)模塊:

insmod:最簡(jiǎn)單的模塊加載命令,但不能解決模塊的依賴關(guān)系modprobe:會(huì)分析模塊的依賴關(guān)系,將所有的依賴模塊都加載到內(nèi)核中

卸載驅(qū)動(dòng)也有兩種命令:

rmmod:最簡(jiǎn)單的模塊卸載命令modprobe -r:除了卸載指定的驅(qū)動(dòng),還卸載其所依賴的其他模塊,若依賴模塊還在被其它模塊使用,就不能使用該命令來卸載驅(qū)動(dòng)模塊

3.2 注冊(cè)與注銷

對(duì)于字符設(shè)備驅(qū)動(dòng)而言,當(dāng)驅(qū)動(dòng)模塊加載成功以后需要注冊(cè)字符設(shè)備,卸載驅(qū)動(dòng)模塊時(shí)也要注銷掉字符設(shè)備。 字符設(shè)備的注冊(cè)和注銷函數(shù)原型如下所示:

static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)//major:主設(shè)備號(hào)//name:設(shè)備名字,指向一串字符串//fops:結(jié)構(gòu)體 file_operations 類型指針,指向設(shè)備的操作函數(shù)集合變量static inline void unregister_chrdev(unsigned int major, const char *name)//major:要注銷的設(shè)備對(duì)應(yīng)的主設(shè)備號(hào)//name:要注銷的設(shè)備對(duì)應(yīng)的設(shè)備名

一般字符設(shè)備的注冊(cè)在驅(qū)動(dòng)模塊的入口函數(shù)xxx_init中進(jìn)行,字符設(shè)備的注銷在驅(qū)動(dòng)模塊的出口函數(shù)xxx_exit中進(jìn)行

//定義了一個(gè)file_operations結(jié)構(gòu)體變量,就是設(shè)備的操作函數(shù)集合static struct file_operations test_fops;/* 驅(qū)動(dòng)入口函數(shù) */static int __init xxx_init(void){  /* 入口函數(shù)具體內(nèi)容 */  int retvalue = 0;  /* 注冊(cè)字符設(shè)備驅(qū)動(dòng) */  retvalue = register_chrdev(200, "chrtest", &test_fops);  if(retvalue < 0){    /* 字符設(shè)備注冊(cè)失敗,自行處理 */  }  return 0;}/* 驅(qū)動(dòng)出口函數(shù) */static void __exit xxx_exit(void){  /* 注銷字符設(shè)備驅(qū)動(dòng) */  unregister_chrdev(200, "chrtest");}/* 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */module_init(xxx_init);module_exit(xxx_exit);

3.3 實(shí)現(xiàn)設(shè)備的具體操作函數(shù)

file_operations結(jié)構(gòu)體就是設(shè)備的具體操作函數(shù)。 假設(shè)對(duì)chrtest這個(gè)設(shè)備有如下兩個(gè)要求:

能夠?qū)崿F(xiàn)打開和關(guān)閉操作:需要實(shí)現(xiàn)open和release這兩個(gè)函數(shù)能夠?qū)崿F(xiàn)進(jìn)行讀寫操作:需要實(shí)現(xiàn)read和write這兩個(gè)函數(shù)

實(shí)現(xiàn)file_operations中的這四個(gè)函數(shù),完成后的內(nèi)容框架如下所示:

/* 打開設(shè)備 */static int chrtest_open(struct inode *inode, struct file *filp){  /* 用戶實(shí)現(xiàn)具體功能 */  return 0;}/* 從設(shè)備讀取 */static ssize_t chrtest_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){  /* 用戶實(shí)現(xiàn)具體功能 */  return 0;}/* 向設(shè)備寫數(shù)據(jù) */static ssize_t chrtest_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){  /* 用戶實(shí)現(xiàn)具體功能 */  return 0;}/* 關(guān)閉/釋放設(shè)備 */static int chrtest_release(struct inode *inode, struct file *filp){  /* 用戶實(shí)現(xiàn)具體功能 */  return 0;}

然后是驅(qū)動(dòng)的入口(init)和出口(exit) 函數(shù):

//定義了一個(gè)file_operations結(jié)構(gòu)體變量test_fops,就是設(shè)備的操作函數(shù)集合static struct file_operations test_fops = {  .owner = THIS_MODULE,  .open = chrtest_open,  .read = chrtest_read,  .write = chrtest_write,  .release = chrtest_release,}/* 驅(qū)動(dòng)入口函數(shù) */static int __init xxx_init(void){  /* 入口函數(shù)具體內(nèi)容 */  int retvalue = 0;  /* 注冊(cè)字符設(shè)備驅(qū)動(dòng) */  retvalue = register_chrdev(200, "chrtest", &test_fops);  if(retvalue < 0){    /* 字符設(shè)備注冊(cè)失敗,自行處理 */  }  return 0;}/* 驅(qū)動(dòng)出口函數(shù) */static void __exit xxx_exit(void){  /* 注銷字符設(shè)備驅(qū)動(dòng) */  unregister_chrdev(200, "chrtest");}/* 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */module_init(xxx_init);module_exit(xxx_exit);

3.4 添加LICENSE和作者信息

LICENSE是必須添加的,否則編譯時(shí)會(huì)報(bào)錯(cuò),作者信息可加可不加

MODULE_LICENSE() //添加模塊 LICENSE 信息MODULE_AUTHOR() //添加模塊作者信息

綜上所述,字符設(shè)備驅(qū)動(dòng)開發(fā)流程如下圖所示:

4. 字符設(shè)備驅(qū)動(dòng)開發(fā)實(shí)驗(yàn)

下面以正點(diǎn)原子的IMX6ULL開發(fā)板為平臺(tái),完整的編寫一個(gè)虛擬字符設(shè)備驅(qū)動(dòng)模塊。 chrdevbase不是實(shí)際存在的一個(gè)設(shè)備,只是為了學(xué)習(xí)字符設(shè)備的開發(fā)的流程

4.1 驅(qū)動(dòng)程序編寫

宏定義及變量定義

#include #include #include #include #include #include #define CHRDEVBASE_MAJOR    200 /* 主設(shè)備號(hào) */#define CHRDEVBASE_NAME    "chrdevbase" /* 設(shè)備名   */static char readbuf[100]; /* 讀緩沖區(qū) */static char writebuf[100]; /* 寫緩沖區(qū) */static char kerneldata[] = {"kernel data!"};

打開、關(guān)閉、讀取、寫入函數(shù)實(shí)現(xiàn)

staticintchrdevbase_open(structinode*inode,structfile*filp){  printk("chrdevbase open!\\r\\n");  return0;}staticssize_tchrdevbase_read(structfile*filp,char __user *buf,size_t cnt,loff_t*offt){  int retvalue =0;  /* 向用戶空間發(fā)送數(shù)據(jù) */  memcpy(readbuf, kerneldata,sizeof(kerneldata));  retvalue =copy_to_user(buf, readbuf, cnt);  if(retvalue ==0){    printk("kernel senddata ok!\\r\\n");  }else{    printk("kernel senddata failed!\\r\\n");  }  printk("chrdevbase read!\\r\\n");  return0;}staticssize_tchrdevbase_write(structfile*filp,constchar __user *buf,size_t cnt,loff_t*offt){  int retvalue =0;  /* 接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來 */  retvalue =copy_from_user(writebuf, buf, cnt);  if(retvalue ==0){    printk("kernel recevdata:%s\\r\\n", writebuf);  }else{    printk("kernel recevdata failed!\\r\\n");  }  printk("chrdevbase write!\\r\\n");  return0;}staticintchrdevbase_release(structinode*inode,structfile*filp){printk("chrdevbase release!\\r\\n");return0;}

驅(qū)動(dòng)加載與注銷

staticstructfile_operations chrdevbase_fops ={  .owner = THIS_MODULE,  .open = chrdevbase_open,  .read = chrdevbase_read,  .write = chrdevbase_write,  .release = chrdevbase_release,};/*驅(qū)動(dòng)入口函數(shù)  */staticint __init chrdevbase_init(void){  int retvalue =0;  retvalue =register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME,&chrdevbase_fops);  if(retvalue <0){    printk("chrdevbase driver register failed\\r\\n");  }  printk("chrdevbase init!\\r\\n");  return0;}/* 驅(qū)動(dòng)出口函數(shù) */staticvoid __exit chrdevbase_exit(void){unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\\r\\n");}/* 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */module_init(chrdevbase_init);module_exit(chrdevbase_exit);

LICENSE與作者

MODULE_LICENSE("GPL");MODULE_AUTHOR("andyxi");

4.2 應(yīng)用程序編寫

應(yīng)用程序運(yùn)行在用戶空間,其通過輸入相應(yīng)的指令來對(duì)chrdevbase設(shè)備執(zhí)行讀或者寫操作。 下面將程序進(jìn)行分段介紹

頭文件和main函數(shù)入口,以及main函數(shù)的傳參處理

#include "stdio.h"#include "unistd.h"#include "sys/types.h"#include "sys/stat.h"#include "fcntl.h"#include "stdlib.h"#include "string.h"static char usrdata[] = {"usr data!"};int main(int argc, char *argv[]){  int fd, retvalue;  char *filename;  char readbuf[100], writebuf[100];  if(argc != 3){    printf("Error Usage!\\r\\n");    return -1;  }  filename = argv[1];  /* 打開驅(qū)動(dòng)文件 */  fd  = open(filename, O_RDWR);  if(fd < 0){    printf("Can"t open file %s\\r\\n", filename);    return -1;  }

對(duì) chrdevbase 設(shè)備的具體操作

if(atoi(argv[2])==1){/* 從驅(qū)動(dòng)文件讀取數(shù)據(jù) */    retvalue =read(fd, readbuf,50);    if(retvalue <0){      printf("read file %s failed!\\r\\n", filename);    }else{      /* 讀取成功,打印出讀取成功的數(shù)據(jù) */      printf("read data:%s\\r\\n",readbuf);    }  }  if(atoi(argv[2])==2){    /* 向設(shè)備驅(qū)動(dòng)寫數(shù)據(jù) */    memcpy(writebuf, usrdata,sizeof(usrdata));    retvalue =write(fd, writebuf,50);    if(retvalue <0){      printf("write file %s failed!\\r\\n", filename);    }  }

關(guān)閉設(shè)備

/* 關(guān)閉設(shè)備 */  retvalue = close(fd);  if(retvalue < 0){    printf("Can"t close file %s\\r\\n", filename);    return -1;  }  return 0;}

4.3 程序編譯

程序編譯包括驅(qū)動(dòng)程序編譯和應(yīng)用程序編譯兩個(gè)部分

驅(qū)動(dòng)程序編譯:將驅(qū)動(dòng)程序編譯為.ko模塊

創(chuàng)建Makefile文件

# KERNELDIR:開發(fā)板所使用的Linux內(nèi)核源碼目錄KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi# CURRENT_PATH:當(dāng)前路徑,通過運(yùn)行“pwd”命令獲取CURRENT_PATH := $(shell pwd)# obj-m:將 chrdevbase.c 這個(gè)文件編譯為chrdevbase.ko模塊obj-m := chrdevbase.obuild: kernel_modules# -C 表示切換工作目錄到KERNERLDIR目錄# M 表示模塊源碼目錄# modules 表示編譯模塊kernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

輸入make命令即可編譯,編譯成功以后就會(huì)生成一個(gè)叫做 chrdevbaes.ko 的文件,此文件就是 chrdevbase 設(shè)備的驅(qū)動(dòng)模塊

注意:若直接make編譯可能會(huì)出錯(cuò),是因?yàn)閗ernel中沒有指定編譯器和架構(gòu),使用了默認(rèn)的x86平臺(tái)編譯報(bào)錯(cuò)。 解決辦法就是在內(nèi)核頂層Makefile中,直接定義ARCH和CROSS_COMPILE這兩個(gè)的變量值為 arm和 arm-linux-gnueabihf- 即可

應(yīng)用程序編譯:無需內(nèi)核參與,直接編譯即可

arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp

使用file命令,查看生成的chrdevbaseApp文件信息,如下圖示,文件是32位LSB格式,ARM版本的,因此只能在ARM芯片下運(yùn)行

4.4 運(yùn)行測(cè)試

為了方便測(cè)試,Linux系統(tǒng)選擇通過TFTP從網(wǎng)絡(luò)啟動(dòng),并且使用NFS掛載網(wǎng)絡(luò)根文件系統(tǒng)。 確保開發(fā)板系統(tǒng)移植成功,能正常啟動(dòng)。 具體的實(shí)現(xiàn)方法可參考之前介紹過的系統(tǒng)移植專輯系列文章

加載驅(qū)動(dòng)模塊

在根文件系統(tǒng)創(chuàng)建/lib/modules/4.1.15文件夾,用來存放驅(qū)動(dòng)模塊

/lib/modules是通用的4.1.15根據(jù)所使用的內(nèi)核版本來設(shè)置,否則modprobe命令無法加載驅(qū)動(dòng)模塊

在Ubuntu中將chrdevbase.ko和chrdevbaseAPP,復(fù)制到根文件系統(tǒng)的 rootfs/lib/modules/4.1.15 目錄中

在開發(fā)板中使用insmod或modprobe命令來加載驅(qū)動(dòng)文件

輸入lsmod命令即可查看當(dāng)前系統(tǒng)中存在的模塊,輸入cat /proc/devices命令,查看當(dāng)前系統(tǒng)中有沒有chrdevbase 這個(gè)設(shè)備

創(chuàng)建設(shè)備節(jié)點(diǎn)文件:驅(qū)動(dòng)加載成功后,需要在/dev目錄下創(chuàng)建一個(gè)與之對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)文件,應(yīng)用程序通過操作這個(gè)設(shè)備節(jié)點(diǎn)文件來完成對(duì)具體設(shè)備的操作

使用mknod命令創(chuàng)建/dev/chrdevbase設(shè)備節(jié)點(diǎn)文件

mknod /dev/chrdevbase c 200 0#/dev/chrdevbase 是要?jiǎng)?chuàng)建的節(jié)點(diǎn)文件# c 表示這是個(gè)字符設(shè)備# 200 是設(shè)備的主設(shè)備號(hào)# 0 是設(shè)備的次設(shè)備號(hào)

創(chuàng)建完后可使用ls /dev/chrdevbase -l命令查看是否存在

操作設(shè)備測(cè)試:使用應(yīng)用程序讀寫設(shè)備,對(duì)/dev/chrdevbase文件進(jìn)行讀寫操作

# 讀操作命令./chrdevbaseApp /dev/chrdevbase 1# 輸出“ kernel senddata ok!”是驅(qū)動(dòng)程序中chrdevbase_read函數(shù)輸出的信息# “read data:kernel data!”就是chrdevbaseAPP打印出來的接收到的數(shù)據(jù)# 寫操作命令./chrdevbaseApp /dev/chrdevbase 2# “kernel recevdata:usr data!”,是驅(qū)動(dòng)程序中的chrdevbase_write函數(shù)輸出的

卸載驅(qū)動(dòng)模塊:若不再使用某個(gè)設(shè)備的話可以將其驅(qū)動(dòng)卸載掉。 輸入rmmod命令卸載驅(qū)動(dòng)后,使用lsmod命令查看chrdevbase這個(gè)模塊還存不存在

至此,Linux字符設(shè)備驅(qū)動(dòng)開發(fā)完成。 本文介紹了驅(qū)動(dòng)開發(fā)中的字符驅(qū)動(dòng)開發(fā)的基本模式,并使用一個(gè)虛擬的字符設(shè)備驅(qū)動(dòng)進(jìn)行測(cè)試,了解驅(qū)動(dòng)程序與應(yīng)用程序之間的調(diào)用關(guān)系。

標(biāo)簽:

上一篇:
下一篇: