<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      內核驅動結構(Part II)

      Structure of Linux Kernel Device Driver

      ref. https://www.youtube.com/watch?v=XoYkHUnmpQo&list=LL&index=1&t=272s

      Talk to the hardware

      在操作系統中有幾種機制能夠讓CPU與硬件設備進行通信:

      • Port I/O: 使用一個專用的總線進行用心
      • Memory-mapped I/O: 與硬件設備共享內存地址空間進行通信,(have the I/O devices mapped to our address spaces, so in the address space you have the registers there where you can talk to the hardware ),這種方法更為常見

      在Memory-Mapped I/O(MMIO)機制中,將I/O設備映射到內存地址空間,然后CPU可以通過寄存器與硬件進行通信,所以當用戶使用指針向某個地址寫入數據,實際上是將數據寫入了寄存器中。

      使用mmio與硬件設備進行通信的三個步驟:

      1. 向MMIO寄存器發送請求,通過一些內核API完成,比如request_mem_region(), it's recommended, not mandatory
      2. 將寄存器的物理地址映射到虛擬地址,比如使用函數ioremap()
      3. 使用內核API讀寫寄存器,比如readb()\writeb(), readw()\writew(), readl()\writel(), readq()\writeq(),分別進行8-bit, 16-bit, 32-bit以及64-bit的讀寫

      當然也可以使用函數ioremap()返回的指針進行讀寫,不過推薦使用內核封裝的函數對這個指針進行操作,注意使用iounmap()釋放掉這些地址空間。完成地址映射后,可以通過cat \proc\iomem來查看I/O設備的地址映射。

      下面這段代碼用于控制一個LED燈設備的驅動:

      #define GPIO1_BASE	0x0209C000
      #define GPIO1_SIZE	8
      #define LED_OFF	0
      #define LED_ON	1
      
      static struct {
      	dev_t devnum;
      	struct cdev cdev;
      	void __iomem *regbase;
          // device datas area
      } drvled_data;
      
      static void drvled_setled(unsigned int status)
      {
      	u32 val;
      
      	/* set value */
      	val = readl(drvled_data.regbase);
      	if (status == LED_ON)
      		val |= GPIO_BIT;
      	else if (status == LED_OFF)
      		val &= ~GPIO_BIT;
      	writel(val, drvled_data.regbase);
      
      	/* update status */
      	drvled_data.led_status = status;
      }
      
      static ssize_t my_write(struct file *file, const char __user *buf,
      			    size_t count, loff_t *ppos)
      {
      	char kbuf = 0;
      	if (copy_from_user(&kbuf, buf, 1))
      		return -EFAULT;
      
      	if (kbuf == '1') {
      		drvled_setled(LED_ON);
      		pr_info("LED ON!\n");
      	} else if (kbuf == '0') {
      		drvled_setled(LED_OFF);
      		pr_info("LED OFF!\n");
      	}
      	return count;
      }
      
      static const struct file_operations drvled_fops = {
      	.owner = THIS_MODULE,
      	.write = my_write,
      };
      
      static int __init init_module(void)
      {
          
      	if (!request_mem_region(GPIO1_BASE, GPIO1_SIZE, "my_device_driver")) {
      		// handle error
      	}
      
      	drvled_data.regbase = ioremap(GPIO1_BASE, GPIO1_SIZE);
      	if (!drvled_data.regbase) {
      		// handle error
      	}
      
          // Device driver initialization and installation
      
          return 0;
      }
      
      static int __exit exit_module(void)
      {
          iounmap(drvled_data.regbase);
      	release_mem_region(GPIO1_BASE, GPIO1_SIZE);
          // Unloading and unregistering the device driver
      }
      

      通過上面的這些API,可以通過讀寫對應了設備文件控制對應的硬件設備。下圖是一個LED的驅動,其整體框架如下:

      led driver 1

      這樣的框架存在一些問題:

      1. 用戶訪問設備文件所使用的接口是自定義的,而沒有進行標準化
      2. 從GPIO控制器中為設備驅動分配了兩個寄存器,那么其他GPIO將無法訪問這兩個寄存器,
        假設GPIO控制器中有32個GPIO,那么沒有人能夠使用其余的另外31個GPIO
      3. 在設備驅動中采用硬編碼的方式寫入硬件相關的信息,那么如果修改了硬件,也必須修改驅動

      因此,這樣的框架還需要一定程度的解耦合并進行模塊化。

      Driver Model

      Linux驅動模型提供了多個設備驅動抽象(abstraction to device drivers),這能夠使驅動代碼更模塊化、可重用并且容易維護。

      該驅動模型的組成如下:

      • Framework:根據設備類型導出的標準化接口
      • Buses:從設備驅動中抽象出來的設備信息以及設備所連接的位置

      led driver 2

      使用驅動框架(linux/leds.h for led device)將接口標準化后,意味著驅動開發者不再需要定義file_operations來指定回調函數的行為,這些接口以及對應回調函數都由對應的框架完成定義。Users know beforehand the interface provided by a driver based on its class or type.

      為了避免硬件資源被一個設備獨占,需要使用特定的API進行動態控制,比如對于LED設備,Linux內核中實現了一種生產者/消費者的模型(gpiolib)來管理GPIO資源。

      • GPIO producer,類似于GPIO控制器的驅動
      • GPIO consumer,類似于LED設備的驅動

      如下,使用了框架將用戶接口標準化,并使用內核接口管理硬件資源:

      #include <linux/leds.h>
      // other header files
      
      struct drvled_data_st {
      	struct gpio_desc *desc;         // change from "void __iomem *regbase;"
      	struct led_classdev led_cdev;   // change from "cdev"
      };
      
      static struct drvled_data_st *drvled_data;
      
      static void drvled_setled(unsigned int status)
      {
          // not use the writel()
      	if (status == LED_ON)
      		gpiod_set_value(drvled_data->desc, 1);
      	else
      		gpiod_set_value(drvled_data->desc, 0);
      }
      
      static void drvled_change_state(struct led_classdev *led_cdev,
      				enum led_brightness brightness)
      {
      	if (brightness)
      		drvled_setled(LED_ON);
      	else
      		drvled_setled(LED_OFF);
      }
      
      static int __init drvled_init(void)
      {
      	int result = 0;
      
          // no need for driver initialization
          // no need for iommp for device
      
      	drvled_data = kzalloc(sizeof(*drvled_data), GFP_KERNEL);
      	if (!drvled_data) {
      		// handle error
      	}
      
      	result = gpio_request(GPIO_NUM, DRIVER_NAME);
      	if (result) {
      		// handle error
      	}
      
      	drvled_data->desc = gpio_to_desc(GPIO_NUM);
      
      	drvled_data->led_cdev.name = "ipe:red:user";
      	drvled_data->led_cdev.brightness_set = drvled_change_state;
      
      	result = led_classdev_register(NULL, &drvled_data->led_cdev);
      	if (result) {
      		// handle error
      	}
          // ...
      }
      
      static void __exit drvled_exit(void)
      {
      	led_classdev_unregister(&drvled_data->led_cdev);
          // not use the iounmap()	
          gpio_free(GPIO_NUM);
      	release_mem_region(GPIO1_BASE, GPIO1_SIZE);
      	kfree(drvled_data);
      }
      // ...
      

      使用設備框架對驅動進行重組后,用戶不再直接與/dev/目錄下的設備文件進行交互,而是在目錄/sys/class/led目錄下找到所有的LED類的設備,進入所注冊的驅動目錄下,可以找到控制LED設備的接口,這些接口仍然以文件的形式存在,但是和之前所定義的接口相比會更加標準化,也就是幾乎所有的LED設備都可以使用這樣的接口進行控制。

      Bus infrastructure

      最后,可以使用總線框架實現設備與驅動的解耦合。總線框架組成如下:

      • Bus Core: 對于給定總線類型(USB core, PCI core, etc)所實現的API, (represented in the kernel by the "bus_type" structure)
      • Bus adapters:總線控制器驅動, (represented in the kernel by the "device_driver" structure)
      • Bus drivers: 負責管理連接到總線的設備, (represented in the kernel by the "device_driver" structure)
      • Bus devices: 所有連接到總線的設備, (represented in the kernel by the structure "device")

      總線框架如下圖所示:

      解耦合的實現:在總線框架中,驅動相當于是一種類(class),當用戶在總線上注冊設備時,將會產生這個類的實例。以I2C總線設備為例,下圖演示了這個過程:

      這個過程可以大致分為三步:

      1. 在Bus Core注冊驅動
      2. 在Bus Core注冊設備,隨后Bus Core將會匹配對應的驅動
      3. 匹配成功后,Bus Core將會通過probe()函數調用驅動對應的回調函數,進行實例化

      有很多種方法向總線注冊一個設備:

      • 使用Bus Core提供的接口,在用戶應用中靜態注冊一個設備,比如I2C Bus提供的i2c_register_board_info(),或者虛擬總線Platform Bus提供的platform_device_register()
      • 使用硬件平臺提供的注冊機制,比如X86提供的ACPI
      • 使用設備樹,比如PowerPC以及ARM提供的標準化機制
      • 有一些總線支持通過設備枚舉(device enumeration)來自動添加設備,比如PCI總線的lspci命令
      posted @ 2024-07-16 12:18  2hYan9  閱讀(138)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产suv精品一区二区四| 亚洲av成人无网码天堂| 999精品色在线播放| 日本深夜福利在线观看| 亚洲成av人片无码迅雷下载| 国产成人午夜精品影院| 美女18禁一区二区三区视频| 黑人好猛厉害爽受不了好大撑| 日本老熟女一二三区视频| 亚洲老熟女一区二区三区 | 久久a级片| 免费无码高H视频在线观看| 精品少妇无码一区二区三批| 18禁免费无码无遮挡不卡网站| 午夜福利日本一区二区无码| 一区二区三区av在线观看| av天堂久久天堂av| 青青青视频免费一区二区| 国产av综合影院| 午夜视频免费试看| 欧洲无码一区二区三区在线观看| 国产精品久久久福利| 亚洲人成网站在线观看播放不卡| 国产亚洲精品综合一区二区| 揭阳市| 亚洲东京色一区二区三区| 国产伦码精品一区二区| 无码国模国产在线观看免费| 爱性久久久久久久久| 国产精品久久久天天影视香蕉 | 中文国产成人精品久久不卡 | 亚洲成av人片不卡无码手机版| 昭通市| 亚洲中文字幕日产无码成人片| 久久99久国产精品66| 欧美激情视频一区二区三区免费 | 国产盗摄视频一区二区三区| 免费国产一区二区不卡| 色成年激情久久综合国产| 久久人爽人人爽人人片av| 精品无码老熟妇magnet|