亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 服務器 > Linux服務器 > 正文

Linux 字符設備驅動框架詳細介紹

2024-09-05 23:03:11
字體:
來源:轉載
供稿:網友

Linux 字符設備驅動框架

字符設備是Linux三大設備之一(另外兩種是塊設備,網絡設備),字符設備就是字節流形式通訊的I/O設備,絕大部分設備都是字符設備,常見的字符設備包括鼠標、鍵盤、顯示器、串口等等,當我們執行ls -l /dev的時候,就能看到大量的設備文件,c就是字符設備,b就是塊設備,網絡設備沒有對應的設備文件。編寫一個外部模塊的字符設備驅動,除了要實現編寫一個模塊所需要的代碼之外,還需要編寫作為一個字符設備的代碼。

驅動模型

Linux一切皆文件,那么作為一個設備文件,它的操作方法接口封裝在struct file_operations,當我們寫一個驅動的時候,一定要實現相應的接口,這樣才能使這個驅動可用,Linux的內核中大量使用"注冊+回調"機制進行驅動程序的編寫,所謂注冊回調,簡單的理解,就是當我們open一個設備文件的時候,其實是通過VFS找到相應的inode,并執行此前創建這個設備文件時注冊在inode中的open函數,其他函數也是如此,所以,為了讓我們寫的驅動能夠正常的被應用程序操作,首先要做的就是實現相應的方法,然后再創建相應的設備文件。

#include <linux/cdev.h> //for struct cdev#include <linux/fs.h>  //for struct file#include <asm-generic/uaccess.h>  //for copy_to_user#include <linux/errno.h>      //for error number/* 準備操作方法集 *//* struct file_operations {  struct module *owner;  //THIS_MODULE    //讀設備  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  //寫設備  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);  //映射內核空間到用戶空間  int (*mmap) (struct file *, struct vm_area_struct *);  //讀寫設備參數、讀設備狀態、控制設備  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);  //打開設備  int (*open) (struct inode *, struct file *);  //關閉設備  int (*release) (struct inode *, struct file *);  //刷新設備  int (*flush) (struct file *, fl_owner_t id);  //文件定位  loff_t (*llseek) (struct file *, loff_t, int);  //異步通知  int (*fasync) (int, struct file *, int);  //POLL機制  unsigned int (*poll) (struct file *, struct poll_table_struct *);  。。。};*/ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset){  return 0;}struct file fops = {  .owner = THIS_MODULE,  .read = myread,  ...};/* 字符設備對象類型 */struct cdev {  //public    struct module *owner;        //模塊所有者(THIS_MODULE),用于模塊計數  const struct file_operations *ops; //操作方法集(分工:打開、關閉、讀/寫、...)  dev_t dev;             //設備號(第一個)  unsigned int count;         //設備數量  //private  ...};static int __init chrdev_init(void){  ...  /* 構造cdev設備對象 */  struct cdev *cdev_alloc(void);  /* 初始化cdev設備對象 */  void cdev_init(struct cdev*, const struct file_opeartions*);  /* 為字符設備靜態申請設備號 */  int register_chedev_region(dev_t from, unsigned count, const char* name);  /* 為字符設備動態申請主設備號 */  int alloc_chedev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);  MKDEV(ma,mi)  //將主設備號和次設備號組合成設備號  MAJOR(dev)   //從dev_t數據中得到主設備號  MINOR(dev)   //從dev_t數據中得到次設備號  /* 注冊字符設備對象cdev到內核 */  int cdev_add(struct cdev* , dev_t, unsigned);  ...}static void __exit chrdev_exit(void){  ...  /* 從內核注銷cdev設備對象 */  void cdev_del(struct cdev* );  /* 從內核注銷cdev設備對象 */  void cdev_put(stuct cdev *);  /* 回收設備號 */  void unregister_chrdev_region(dev_t from, unsigned count);  ...}

實現read,write

Linux下各個進程都有自己獨立的進程空間,即使是將內核的數據映射到用戶進程,該數據的PID也會自動轉變為該用戶進程的PID,由于這種機制的存在,我們不能直接將數據從內核空間和用戶空間進行拷貝,而需要專門的拷貝數據函數/宏:

long copy_from_user(void *to, const void __user * from, unsigned long n)long copy_to_user(void __user *to, const void *from, unsigned long n)

這兩個函數可以將內核空間的數據拷貝到回調該函數的用戶進程的用戶進程空間,有了這兩個函數,內核中的read,write就可以實現內核空間和用戶空間的數據拷貝。

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset){  long ret = 0;  size = size > MAX_KBUF?MAX_KBUF:size;  if(copy_to_user(user_buf, kbuf,size)    return -EAGAIN;  }  return 0;}

實現ioctl

ioctl是Linux專門為用戶層控制設備設計的系統調用接口,這個接口具有極大的靈活性,我們的設備打算讓用戶通過哪些命令實現哪些功能,都可以通過它來實現,ioctl在操作方法集中對應的函數指針是long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);,其中的命令和參數完全由驅動指定,Linux建議如圖所示的方式定義ioctl()命令

設備類型    序列號     方向      數據尺寸
8bit             8bit           2bit      13/14bit

這里,設備類型字段為一個幻數,可以是0~0xff之間的數,內核中的"ioctl-number.txt"給出了一個推薦的和已經被使用的幻數(但是已經好久沒人維護了),新設備驅動定義幻數的時候要避免與其沖突。命令碼的方向字段為2bit,表示數據的傳輸方向,可能的值是:_IOC_NONE,_IOC_READ,_IOC_WRITE和_IOC_READ|_IOC_WRITE。命令碼的數據字段表示涉及的用戶數據的大小,這個成員的寬度依賴于體系結構,通常是13或14位。內核還定義了_IO(),_IOR(),_IOW(),_IOWR()這4個宏來輔助生成這種格式的命令。這幾個宏的作用是根據傳入的type(設備類型字段),nr(序列號字段)和size(數據長度字段)和宏名銀行的方向字段移位組合生成命令碼。內核中還預定義了一些I/O控制命令,如果某設備驅動中包含了與預定義命令一樣的命令碼,這些命令會被當做預定義命令被內核處理而不是被設備驅動處理,有如下4種:

  1. FIOCLEX:即file ioctl close on exec 對文件設置專用的標志,通知內核當exec()系統帶哦用發生時自動關閉打開的文件
  2. FIONCLEX:即file ioctl not close on exec,清除由FIOCLEX設置的標志
  3. FIOQSIZE:獲得一個文件或目錄的大小,當用于設備文件時,返回一個ENOTTY錯誤
  4. FIONBIO:即file ioctl non-blocking I/O 這個調用修改flip->f_flags中的O_NONBLOCK標志

我們可以將驅動設計的命令包含在一個頭文件中,記錄用戶程序和驅動程序的命令約定,下面是一個簡單的例子

//mycmd.h...#include <asm/ioctl.h>#define CMDT 'A'#define KARG_SIZE 36struct karg{  int kval;  char kbuf[KARG_SIZE];};#define CMD_OFF _IO(CMDT,0)#define CMD_ON _IO(CMDT,1)#define CMD_R  _IOR(CMDT,2,struct karg)#define CMD_W  _IOW(CMDT,3,struct karg)...
//chrdev.cstatic long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){  static struct karg karg = {    .kval = 0,    .kbuf = {0},  };  struct karg *usr_arg;  switch(cmd){  case CMD_ON:    /* 開燈 */    break;  case CMD_OFF:    /* 關燈 */    break;  case CMD_R:    if(_IOC_SIZE(cmd) != sizeof(karg)){      return -EINVAL;    }    usr_arg = (struct karg *)arg;    if(copy_to_user(usr_arg, &karg, sizeof(karg))){      return -EAGAIN;    }    break;  case CMD_W:       if(_IOC_SIZE(cmd) != sizeof(karg)){      return -EINVAL;    }    usr_arg = (struct karg *)arg;    if(copy_from_user(&karg, usr_arg, sizeof(karg))){      return -EAGAIN;    }    break;  default:    ;  };  return 0;}

創建設備文件

插入的設備模塊,我們就可以使用cat /proc/devices命令查看當前系統注冊的設備,但是我們還沒有創建相應的設備文件,用戶也就不能通過文件訪問這個設備。設備文件的inode應該是包含了這個設備的設備號,操作方法集指針等信息,這樣我們就可以通過設備文件找到相應的inode進而訪問設備。創建設備文件的方法有兩種,手動創建或自動創建,手動創建設備文件就是使用mknod /dev/xxx 設備類型 主設備號 次設備號的命令創建,所以首先需要使用cat /proc/devices查看設備的主設備號并通過源碼找到設備的次設備號,需要注意的是,理論上設備文件可以放置在任何文件加夾,但是放到"/dev"才符合Linux的設備管理機制,這里面的devtmpfs是專門設計用來管理設備文件的文件系統。設備文件創建好之后就會和創建時指定的設備綁定,即使設備已經被卸載了,如要刪除設備文件,只需要像刪除普通文件一樣rm即可。理論上模塊名(lsmod),設備名(/proc/devices),設備文件名(/dev)并沒有什么關系,完全可以不一樣,但是原則上還是建議將三者進行統一,便于管理。

除了使用蹩腳的手動創建設備節點的方式,我們還可以在設備源碼中使用相應的措施使設備一旦被加載就自動創建設備文件,自動創建設備文件需要我們在編譯內核的時候或制作根文件系統的時候就好相應的配置:

Device Drivers --->    Generic Driver Options --->      [*]Maintain a devtmpfs filesystem to mount at /dev      [*] Automount devtmpfs at /dev,after the kernel mounted the rootfsOR

制作根文件系統的啟動腳本寫入

mount -t sysfs none sysfs /sysmdev -s //udev也行

有了這些準備,只需要導出相應的設備信息到"/sys"就可以按照我們的要求自動創建設備文件。內核給我們提供了相關的API

class_create(owner,name);struct device *device_create_vargs(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, va_list vargs);void class_destroy(struct class *cls);  void device_destroy(struct class *cls, dev_t devt);

有了這幾個函數,我們就可以在設備的xxx_init()和xxx_exit()中分別填寫以下的代碼就可以實現自動的創建刪除設備文件

 /* 在/sys中導出設備類信息 */  cls = class_create(THIS_MODULE,DEV_NAME);  /* 在cls指向的類中創建一組(個)設備文件 */  for(i= minor;i<(minor+cnt);i++){    devp = device_create(cls,NULL,MKDEV(major,i),NULL,"%s%d",DEV_NAME,i);  } 
/* 在cls指向的類中刪除一組(個)設備文件 */  for(i= minor;i<(minor+cnt);i++){    device_destroy(cls,MKDEV(major,i));  }  /* 在/sys中刪除設備類信息 */  class_destroy(cls);       //一定要先卸載device再卸載class

完成了這些工作,一個簡單的字符設備驅動就搭建完成了,現在就可以寫一個用戶程序進行測試了^ - ^

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久99久在线视频| 欧美成人高清视频| 亚洲欧洲xxxx| 久久久精品中文字幕| 国产精品久久久av久久久| 日韩av在线免费| 亚洲剧情一区二区| 亚洲国产精品va在线| 久久久久日韩精品久久久男男| www.99久久热国产日韩欧美.com| 欧洲精品久久久| 日韩大片在线观看视频| 日本久久久久久久久久久| 97精品久久久中文字幕免费| 粉嫩av一区二区三区免费野| 97视频在线观看播放| 亚洲人成电影在线播放| 性夜试看影院91社区| 成人激情视频在线| 久久97精品久久久久久久不卡| 国内揄拍国内精品少妇国语| 亚洲欧洲日产国码av系列天堂| 国产欧美最新羞羞视频在线观看| 欧美激情亚洲另类| 亚洲韩国欧洲国产日产av| 亚洲电影天堂av| 红桃视频成人在线观看| 久久激情五月丁香伊人| 最新国产精品亚洲| 欧美人与性动交a欧美精品| 成人黄色av网站| 亚洲自拍小视频免费观看| 亚洲乱码一区av黑人高潮| 中国日韩欧美久久久久久久久| 欧美精品videosex牲欧美| 日韩电影免费观看在线| 96精品视频在线| 这里只有精品在线观看| 伊人伊人伊人久久| 日韩精品视频在线播放| 日本最新高清不卡中文字幕| 日韩欧美亚洲一二三区| 国产精品露脸自拍| 丝袜一区二区三区| 国产精品18久久久久久麻辣| 精品中文字幕久久久久久| 国产成人精品久久| 久久免费精品视频| 久久五月天色综合| 久久国产精品久久久久久久久久| 日韩av在线网站| 国产91九色视频| 色偷偷91综合久久噜噜| 欧美激情a在线| 日韩免费在线视频| 国产成人精品视频在线观看| 久久精视频免费在线久久完整在线看| 91亚洲一区精品| 日韩有码在线电影| 国产美女久久精品| 国产精品99久久久久久白浆小说| 日本久久久久久| 日韩av手机在线观看| 亚洲男人天堂手机在线| 国内免费精品永久在线视频| 性色av一区二区三区免费| 国产精品午夜一区二区欲梦| 裸体女人亚洲精品一区| 日韩精品视频三区| 欧美精品福利视频| 色无极亚洲影院| 亚洲视频一区二区三区| 亚洲a成v人在线观看| 国产精品jizz在线观看麻豆| 国产精品美女www| 日本久久91av| 欧美裸体男粗大视频在线观看| 国产精品视频最多的网站| 在线观看视频99| 亚洲精品一区二区网址| 91精品国产91| 中文字幕在线成人| 久久久国产一区| 66m—66摸成人免费视频| 亚洲最大福利网| 国产91色在线免费| 日韩精品中文字幕久久臀| 17婷婷久久www| 亚洲精品一区二三区不卡| 亚洲天堂网站在线观看视频| 爱福利视频一区| 中文字幕亚洲欧美日韩2019| 成人观看高清在线观看免费| 久久五月天综合| 欧美成年人视频网站| 亚洲无限乱码一二三四麻| 欧美日韩加勒比精品一区| 欧美精品videosex性欧美| 欧美激情精品久久久久久变态| 成人免费看片视频| 欧美日韩国产成人高清视频| 国产精品尤物福利片在线观看| 色婷婷**av毛片一区| 日韩欧亚中文在线| 亚洲国产免费av| 2019中文字幕在线| 欧美一级淫片丝袜脚交| 久久精品国产久精国产一老狼| 国产亚洲xxx| 亚洲韩国欧洲国产日产av| 日韩中文字幕不卡视频| 色综合男人天堂| 久久中文久久字幕| 久久亚洲精品网站| 91网站在线看| 欧美中文在线观看国产| 在线成人一区二区| 久久久久这里只有精品| 欧美色视频日本版| 久久精品国产清自在天天线| 欧美激情区在线播放| 精品调教chinesegay| xxav国产精品美女主播| 欧美大片免费观看在线观看网站推荐| 在线亚洲国产精品网| 欧美精品aaa| 九九热最新视频//这里只有精品| 国产精品对白刺激| 亚洲香蕉av在线一区二区三区| 欧美xxxx14xxxxx性爽| 日韩成人免费视频| 2021久久精品国产99国产精品| 国产69精品久久久| 欧美国产精品人人做人人爱| 欧美成人精品一区二区三区| 色综合天天狠天天透天天伊人| zzjj国产精品一区二区| 久久色在线播放| 国产日韩欧美电影在线观看| 欧美野外猛男的大粗鳮| 久久精品视频免费播放| 国产精品自拍偷拍| 最近2019中文字幕第三页视频| 91免费看视频.| 91黑丝高跟在线| 国产精品视频不卡| 岛国av一区二区在线在线观看| 97在线观看视频国产| 中文字幕亚洲欧美日韩2019| 成人高h视频在线| 久久手机免费视频| 国产精品96久久久久久| 亚洲午夜久久久影院| 国产中文欧美精品| 欧美精品videosex性欧美| 国产激情视频一区| 91国偷自产一区二区三区的观看方式| 尤物yw午夜国产精品视频明星| 欧美xxxx做受欧美| 国产一区二区丝袜高跟鞋图片| 亚洲国产精品小视频| xvideos成人免费中文版| 国产精品自拍视频| 国产成人精品视频在线观看|