日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux PCI驱动编写

發布時間:2023/12/18 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux PCI驱动编写 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??這幾天將以前為內核2.6寫的驅動移植到了4.1下,在這里記錄一下過程,以及從頭整理一下linux下pci驅動的編寫方法。
??以前的驅動沒有使用到linux下的probe方法,在4.1內核下成功編譯后,一直無法進入中斷,因此參考ch36x的驅動,重新寫了驅動初始化部分,當應用層的程序可以調用驅動正確讀回采集卡數據的時候,那份欣喜與滿足感是難以言表的。

驅動模塊初始化相關函數定義

??PCI驅動和其它linux驅動一樣,需要定義init和exit兩個函數作為加載模塊的入口點和卸載模塊的出口點,可以使用module_pci_driver宏,只要將pci_driver結構體變量作為參數調用這個宏,就會自動定義init和exit這兩個函數,好處是代碼整潔,缺點就是不夠靈活,我這里仍然用傳統的方法使用module_init和module_exit兩個宏:

module_init( init_m ); module_exit( cleanup_m );

??init_m是使用insmod命令加載驅動模塊時調用的函數,cleanup_m是用rmmod卸載模塊時調用的函數。
??在init_m函數中先調用alloc_chrdev_region來給設備分配編號,然后調用class_create函數創建一個設備類,只有調用這個函數后才能夠使用device_create函數在/dev目錄下動態創建設備節點(在probe函數中調用),最后調用pci_register_driver宏把pci專用的結構體pci_driver變量放到pci總線設備組中,讓內核知道有我們這個驅動模塊的存在。

alloc_chrdev_region函數說明如下:
函數原型:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

參數說明:
dev:向內核申請下來的設備號
baseminor :次設備號的起始
count: 申請次設備號的個數
name :執行 cat /proc/devices顯示的名稱,由驅動編寫者定義
返回值:執行成功返回0

class_create函數說明如下:
函數原型:

struct class *class_create(struct module *owner, const char *name);

參數說明:
owner:指定類的所有者是哪個模塊,可以用THIS_MODULE獲取本驅動模塊;
name:類名,由驅動編寫者定義。
返回值:創建的類指針。
可以用IS_ERR判斷返回的類指針是否有錯誤,如果有錯誤,則使用PTR_ERR來返回錯誤代碼。
在cleanup_m函數中注銷掉pci驅動結構體以及之前申請到的設備號。

pci_driver結構體:

這個結構體的聲明在include/linux/pci.h里面:

struct pci_driver { struct list_head node; char *name; const struct pci_device_id *id_table; int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); void (*remove) (struct pci_dev *dev); int (*save_state) (struct pci_dev *dev, u32 state); int (*suspend)(struct pci_dev *dev, u32 state); int (*resume) (struct pci_dev *dev); int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); };

我的驅動中定義的有如下幾項:
name:驅動模塊名稱
id_table:PCI設備的廠商ID,設備ID等
probe:總線發現我們設備的時候調用的函數,對于已經插好的PCI板卡,加載驅動模塊的時候就會調用此函數。
remove:移除設備的時候調用,在卸載驅動模塊的時候也會調用。

probe函數

??在這個函數中要為設備的私有數據結構變量分配空間,使能設備,獲取設備所需的資源,注冊中斷等,其中涉及到的相關函數介紹如下:
pci_enable_device:使能設備的IO和內存空間,如果設備處于掛起狀態則喚醒之。
??pci_request_regions:通知內核設備IO和內存空間已經被指定名稱的設備占用,其它就不要再占用了。
??pci_set_drvdata:設置PCI驅動私有數據,本來這個函數將私有數據結構變量和pci_dev關聯起來了,但是在file_operations相關操作中沒有pci_dev獲取方法,所以我最后極不優雅的使用全局變量來記錄設備私有數據。
??request_irq:注冊中斷
??cdev_init:初始化字符設備的cdev變量
??cdev_add:添加一個字符設備到系統中
??device_create:創建字符設備,這個函數調用完成后,在系統的/dev目錄下將會有函數設定的設備文件出現。

remove函數

??此函數清理了probe函數中所申請的資源,注意清理資源時要和申請資源的順序相反。

驅動模塊文件操作相關函數

??之前的工作是將驅動模塊加載到內核中,并且為相關設備分配資源。接下來要想應用層訪問到設備,操控設備,則需要進行一些文件操作。對于應用程序來講,一個設備也就是一個文件。

file_operations結構體

??這個結構體的聲明在include/linux/fs.h文件中

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 *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);int (*show_fdinfo)(struct seq_file *m, struct file *f); };

??結構體成員函數很多,但是我的驅動只實現了open,release,unlocked_ioctl,read。下面一一介紹。

open函數

??應用層調用open函數時,驅動模塊的open函數就會被調用(有點廢話的感覺),驅動模塊的open函數會傳遞進兩個參數:
??inode結構和file結構,inode結構對于我們比較有用的就是獲取設備索引,獲得設備索引后就可以填充file結構中的private_data成員變量,這個成員變量實際上就是前面probe函數中定義的私有數據成員變量,在我的程序里,我用一個全局指針數組來存放所有設備的私有數據成員指針。偽代碼如下:

int zt_pl2360_open(struct inode *inode,struct file *filp) {int nRet = 0;PZT_PL2360_Dev pd = 0; int nInx = MINOR( inode->i_rdev );pd = g_pZT_PL2360_MyDev[nInx];filp->private_data = pd;return nRet; }

release函數在應用層調用close函數時會被調用,它是open的反向操作,就不多說了。
read函數將內核數據復制到應用層,我這里就是將采集卡讀出的數據交給應用程序。

unlocked_ioctl函數

??這個函數在低版本內核中就是ioctl,對應應用層的ioctl調用,在kernel 2.6.36后使用unlocked_ioctl和compat_ioctl代替,其中unlocked_ioctl在應用層和內核層相同位數的時候調用,compat_ioctl在32位應用,64位內核驅動的時候調用。
??在我的驅動中,這個函數是應用程序與設備通訊的主要函數,設置設備參數,讀回設備狀態等操作均在這個函數中完成。偽代碼如下:

long zt_pl2360_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) {int retval = 0;PZT_PL2360_Dev pd = (PZT_PL2360_Dev)(filp->private_data);if (_IOC_TYPE(cmd) != PL2360_IOC_MAGIC) return -ENOTTY;if (_IOC_NR(cmd) > PL2360_IOC_MAXNR) return -ENOTTY;switch(cmd) {case PL2360_IOC_OUTL:retval = PL2360_IOC_Outl( pd, arg );break;case PL2360_IOC_INL:retval = PL2360_IOC_Inl( pd, arg );break;……default: return -ENOTTY;}return retval; }

??最后在說一點,驅動完成后,我安裝的時候遇到了“-1 Unknown symbol in module”錯誤,需要在文件添加:MODULE_LICENSE(“GPL”);問題解決。

修改設備節點權限,讓非root用戶可以使用

首先看你節點信息:
udevadm info --attribute-walk --name=/dev/pl21080
/dev/pl21080是節點名稱
我的信息:
looking at device ‘/devices/pci0000:00/0000:00:1c.1/0000:02:00.0/0000:03:0c.0/pl2108_class/pl21080’:
KERNEL==“pl21080”
SUBSYSTEM==“pl2108_class”
DRIVER==“”
然后在/etc/udev/rules.d目錄下新建文件
vi 10-myrule.rules
添加
SUBSYSTEM==“pl2108_class”, MODE=“0666”
保存退出
此時再insmod就可以讓普通用戶有權限了

總結

以上是生活随笔為你收集整理的Linux PCI驱动编写的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。