添加内核驱动模块(3)(mydriver.c+ Konfig+Makefile )
之前在注冊SPIDRIVER時,我們注冊了SPI的probe函數和remove函數。
我們來編寫這兩個函數。
當syscall調用該函數時,傳遞一個參數,它是一個struct spi_device型的指針spi。
定義一系列指針,后續作為句柄使用。
判斷SPIMASTER的模式。
spi->master->flags & SPI_MASTER_HALF_DUPLEX
如果是SPI_MASTER_HALF_DUPLEX模式,那么不能使用,打印錯誤信息,然后進入退出處理棧spi_err。
如果不是SPI_MASTER_HALF_DUPLEX模式,那么可以使用,繼續執行。
填充SPIDEVICE描述塊的成員。
mode,bits_per_word。
設置SPIDEVICE的配置信息。
spi_setup()負責對一個SPIDEVICE描述塊進行設置,并返回狀態值。
判斷返回的狀態值。
如果非法,則打印錯誤信息,并進入退出處理棧spi_err
如果合法,則繼續執行。
利用SPIDEVICE扎到對應的PMOD設備描述塊。
將spi->dev.platform_data賦值給gpio_pmodoled_dev,它是一個struct gpio_pmodoled_device型的指針。關聯到PMOD的設備資源后,后續可以用gpio_pmodoled_dev作為句柄。
判斷gpio_pmodoled_dev指針的合法性。
如果為NULL,則非法。打印錯誤信息,然后修改status,后面返回值需要使用status。然后進入退出處理棧spi_platform_data_err。
如果不為NULL,則合法。那么繼續執行。
配置CDEV設備資源。
gpio_pmodoled_setup_cdev()負責配置PMOD中內含的CDEV設備資源,并關聯到SPIDEVICE上。返回狀態值。
判斷返回的狀態值。
如果非法,則打印錯誤信息,并進入退出處理棧cdev_add_err。
如果合法,則繼續執行。
初始化PMOD設備資源中的mutex系統資源。
mutex_init()負責初始化一個mutex描述塊。
初始化PMOD設備資源中用到的GPIO系統資源。
gpio_pmodoled_init_gpio()負責初始化PMOD設備描述塊中使用的GPIO系統資源。返回狀態值。
判斷返回的狀態值的合法性。
如果非法,則打印錯誤信息,并進入退出處理棧oled_init_error。
如果合法,則繼續執行。
初始化PMOD設備資源中用到的顯示緩沖資源。
gpio_pmodoled_disp_init()負責初始化PMOD設備描述塊中使用的顯示資源。
初始化緩沖區。
memset()負責將緩沖區清零。
把初始化好的顯示緩沖區刷到硬件上。
screen_buf_to_display()負責把緩沖區flush到硬件上。返回狀態值。
判斷返回的狀態值的合法性。
如果非法,則打印錯誤信息,并進入退出處理棧oled_init_error。
如果合法,則繼續執行。
至此,全部執行完。正常退出,不需要進入退出處理棧。所以直接return。
再編寫remove函數。
static int __devexit gpio_pmodoled_spi_remove(struct spi_device *spi) {int status;struct gpio_pmodoled_device *dev;uint8_t wr_buf[10];dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;if(dev == NULL) {dev_err(&spi->dev, "spi_remove: Error fetch gpio_pmodoled_device struct\n");return -EINVAL; }#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Clearing Display\n", dev->name); #endif /* Clear Display */memset(dev->disp_buf, 0, DISPLAY_BUF_SZ);status = screen_buf_to_display(dev->disp_buf, dev);/* Turn off display */wr_buf[0] = OLED_DISPLAY_OFF;status = spi_write(spi, wr_buf, 1);if(status) {dev_err(&spi->dev, "oled_spi_remove: Error writing to SPI device\n");}/* Turn off VCC (VBAT) */gpio_set_value(dev->iVBAT, 1);msleep(100);/* TUrn off VDD Power */gpio_set_value(dev->iVDD, 1); #ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Free GPIOs\n", dev->name); #endif { struct gpio gpio_pmodoled_ctrl[] = {{dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat"},{dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD"},{dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET"},{dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C"},};gpio_free_array(gpio_pmodoled_ctrl, 4); }if(&dev->cdev) { #ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name); #endif device_destroy(gpio_pmodoled_class, dev->dev_id);cdev_del(&dev->cdev);}cur_minor--;printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Device Removed\n", dev->name);return status; }當syscall調用remove函數時,會傳遞一個參數,它是一個struct spi_device型的指針。
首先定義一系列指針,后續作為句柄使用。
從SPIDEVICE設備描述塊中,找到PMOD的描述塊。
dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;
將 dev.platform_data賦值給dev,它是一個struct gpio_pmodoled_device型的指針。我們用dev指向了PMOD的描述塊,后續用dev作為句柄。
判斷dev的指針合法性。
如果為NULL,則非法。打印錯誤信息,并直接return.
如果不為NULL,則合法。繼續執行。
初始化顯示緩沖區,memset()完成。
將顯示緩沖區的內容flush到硬件上。
screen_buf_to_display()負責將內容flush到硬件上。
配置wr_buf,然后用SPI發送緩沖區字符。
spi_write()負責寫操作,它利用一個SPIDEVICE設備資源實現寫操作時序。返回狀態值。
判斷返回的狀態值。
如果非法,則打印錯誤信息。由于不是致命錯誤。所以打印錯誤信息后,并不進入退出處理棧。
如果合法,則繼續執行。
設置GPIO。
gpio_set_value()負責將一個在系統中注冊的合法GPIO編號,設置為配置值。
msleep(),睡眠等待。
定義一個臨時數組gpio_pmodoled_ctrl,它是一個struct gpio型的數組。
同是設置多個GPIO的狀態。
gpio_free_array()負責將一個GPIO_ARRAY中GPIO系統資源,同時釋放掉。
判斷PMOD的CDEV的指針合法性。
如果不為NULL,說明CDEV仍占有內核資源,那么釋放掉CDEV的系統資源。
device_destroy()負責注銷CLASS描述塊。它根據dev_id注銷CDEV關聯的CLASS描述塊。
cdev_del()負責注銷CDEV設備資源。它根據傳入的CDEV的描述塊,在設備中進行匹配查找,然后注銷掉系統資源,并取消內核和CDEV描述塊之間的關聯。
我們定義了靜態變量cur_minor。它用來記錄當前系統中存在的CDEV設備數量。
修改cur_minor。
cur_minor–;
至此,全部完成。直接return status.
總結
以上是生活随笔為你收集整理的添加内核驱动模块(3)(mydriver.c+ Konfig+Makefile )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 费曼:“只要我不能创造的,我就还不理解”
- 下一篇: 数模算法:排队论模型