Linux核心開發之簡單字元裝置驅動(上)

來源:互聯網
上載者:User
廢話少說,先來介紹幾個必須要知道的和字元裝置有關的結構體,然後結合代碼詳細講解。
第一部分    必要的裝置結構體
1)linux 2.6核心中使用cdev結構體表示字元裝置:struct cdev{  struct kobject kobj;//內嵌的kobject對象  struct module *owner;//所屬模組  struct file_operations *ops;//檔案操作結構體  struct list_head list;  dev_t dev;//裝置號,長度為32位,其中高12為主裝置號,低20位為此裝置號  unsigned int count;};
可以使用下列宏從dev_t中獲得主次裝置號:                         也可以使用下列宏通過主次裝置號產生dev_t:    MAJOR(dev_t dev);                                                    MKDEV(int major,int minor);    MINOR(dev_t dev);說明:在2.6核心中可以容納大量的裝置,而先前的核心版本卻限於255個主裝置號和255個此裝置號。 2)file_operations結構體中的成員函數是字元裝置驅動程式設計中的主體內容,這些函數實際會在應用程式進行linux的open(),write(),read(),close()等系統調用時被最終調用。目前的file_operations結構已經變得非常大,在這裡我們就關心和我這個裝置程式有關的幾個函數,以後用到了,咱們再提也不遲:
static const struct file_operations globalmem_fops ={   .owner= THIS_MODULE,   .llseek = globalmem_llseek,//修改一個檔案的當前讀寫位置並將新位置返回,出錯時,返回一個負值   .read = globalmem_read,   .write = globalmem_write,   .ioctl = globalmem_ioctl,//裝置相關控制命令的實現,核心可以識別一部分控制命令(這時就不用調用ioctl                             ()),如果裝置不提供這個函數,而核心又不識別該命令,則返回-EINVAL.   .open = globalmem_open,   .release = globalmem_release,};
 
3)file結構與使用者空間的File沒有任何關聯,strcut file是一個核心結構,它不會出現在使用者程式中。它代表一個開啟的檔案(不局限於裝置驅動程式,系統中每個開啟的檔案在核心空間中都有一個對應的file結構)。它由核心在open時建立,並傳遞給在該檔案進行操作的所有函數,直到最後的close函數。在檔案的所有執行個體都被關閉之後,核心會釋放掉這個資料結構。 4)核心用它node結構在內部表示檔案,其和file結構不同。後者表示開啟的檔案描述符。對於單個檔案,可能會有很多表示開啟的檔案描述符的file結構,但它們都指向單個inode結構。在它裡邊和我們驅動程式有用的欄位只有兩個:dev_t i_rdev;    //對錶示裝置檔案的inode結構,該欄位包含了真正的裝置編號struct cdev *i_cdev;     //是表示字元裝置的核心的內部結構。當inode指向一個字元裝置檔案時,該欄位包含了指向struct cdev結構的指標。我麼可以使用下邊兩個宏從inode中獲得主裝置號和此裝置號:
unsigned int iminor(struct inode *inode);unsigned int imajor(struct inode *inode);
為了程式的可移植性,我們應該使用上述宏,而不是直接操作i_rdev。 第二部分 原始碼詳解
必要的標頭檔#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/cdev.h>#include <asm/io.h>#include <asm/system.h>#include <asm/uaccess.h>

#define GLOBALMEM_SIZE 0X1000 /*全域記憶體大小4kb*/ 
#define MEM_CLEAR 0x1 //清零全域記憶體
#define GLOBALMEM_MAJOR 150 //預設的globalmem的主裝置號
static globalmem_major = GLOBALMEM_MAJOR; 
 
//globalmem裝置結構體
struct globalmem_dev
{
    struct cdev cdev;
    unsigned char mem[GLOBALMEM_SIZE];//全域記憶體
};
struct globalmem_dev *globalmem_devp;//裝置結構指標//檔案開啟函數int globalmem_open(struct inode *inode, struct file *filp){filp->private_data= globalmem_devp;return 0;}//檔案釋放函數int globalmem_release(struct inode *inode, struct file *filp){return 0;}//globalmem_ioctl函數static int globalmem_ioctl(struct inode *inodep, struct file *filp,unsigned int cmd, unsigned long arg){struct globalmem_dev *dev = filp->private_data;switch (cmd){case MEM_CLEAR://清除全域記憶體memset(dev->mem, 0,GLOBALMEM_SIZE);printk(KERN_INFO "globalmem is set to zero\n");break;default:return - EINVAL;//其他不支援的命令}return 0;}//globalmem_read函數static ssize_t globalmem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size;int ret = 0;struct globalmem_dev *dev=filp->private_data;//獲得裝置結構指標//分析和擷取有效寫長度if (p >= GLOBALMEM_SIZE)return count ? -ENXIO: 0;if (count > GLOBALMEM_SIZE-p)//如果要求讀取的比實際可用的少count = GLOBALMEM_SIZE-p;if (copy_to_user(buf,(void *)(dev->mem + p),count)){ret = -EFAULT;}else{*ppos += count;ret = count;printk(KERN_INFO "read %d byte(s) from %d",count,p);}return ret;}//globalmem_writestatic ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos){unsigned long p = *ppos;unsigned int count = size; int ret = 0;struct globalmem_dev *dev = filp->private_data;//獲得裝置結構指標//分析和擷取有效寫長度if( p >= GLOBALMEM_SIZE)return count ? -ENXIO: 0;if (count > GLOBALMEM_SIZE-p)//如果要求讀取的比實際可用的少count = GLOBALMEM_SIZE-p;if (copy_from_user(dev->mem + p,buf, count))ret = -EFAULT;else{*ppos+= count;ret = count;printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);}return ret;}//globalmem_seek函數static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig){loff_t ret=0;switch(orig){case 0://從檔案開頭開始位移if(offset < 0){ret = - EINVAL;break;}if((unsigned int)offset > GLOBALMEM_SIZE)//位移越界{ret = - EINVAL;break;}filp->f_pos= (unsigned int)offset;ret = filp->f_pos;break;case 1://從當前位置位移if((filp->f_pos+offset) > GLOBALMEM_SIZE) //位移越界{ret = - EINVAL;break;}if((filp->f_pos+offset)<0){ret = - EINVAL;break;}filp->f_pos += offset;ret = filp->f_pos;break;default:ret = - EINVAL;break;}return ret;}

以上介紹了Linux簡單字元裝置中涉及到的基本而要特別重要的資料結構,還有就是原始碼部分中有關file_operations中的所有操作,這些都將在應用程式進行Linux的

open(),write(),read(),close()等系統調用時最終被調用。這些都是從原始碼中直接copy出來的,無論順序還是結構都保持了實際代碼的完整性,可以和下篇部分完整拷貝下來實際測試使用。

    
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.