国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Ubuntu中為Android系統(tǒng)上編寫Linux內(nèi)核驅(qū)動程序?qū)崿F(xiàn)方法

2019-12-12 05:48:00
字體:
供稿:網(wǎng)友

        在智能手機(jī)時代,每個品牌的手機(jī)都有自己的個性特點。正是依靠這種與眾不同的個性來吸引用戶,營造品牌凝聚力和用戶忠城度,典型的代表非iphone莫屬了。據(jù)統(tǒng)計,截止2011年5月,AppStore的應(yīng)用軟件數(shù)量達(dá)381062個,位居第一,而Android Market的應(yīng)用軟件數(shù)量達(dá)294738,緊隨AppStore后面,并有望在8月份越過AppStore。隨著Android系統(tǒng)逐步擴(kuò)大市場占有率,終端設(shè)備的多樣性亟需更多的移動開發(fā)人員的參與。據(jù)業(yè)內(nèi)統(tǒng)計,Android研發(fā)人才缺口至少30萬。目前,對Android人才需求一類是偏向硬件驅(qū)動的Android人才需求,一類是偏向軟件應(yīng)用的Android人才需求。總的來說,對有志于從事Android硬件驅(qū)動的開發(fā)工程師來說,現(xiàn)在是一個大展拳腳的機(jī)會。那么,就讓我們一起來看看如何為Android系統(tǒng)編寫內(nèi)核驅(qū)動程序吧。

        這里,我們不會為真實的硬件設(shè)備編寫內(nèi)核驅(qū)動程序。為了方便描述為Android系統(tǒng)編寫內(nèi)核驅(qū)動程序的過程,我們使用一個虛擬的硬件設(shè)備,這個設(shè)備只有一個4字節(jié)的寄存器,它可讀可寫。想起我們第一次學(xué)習(xí)程序語言時,都喜歡用“Hello, World”作為例子,這里,我們就把這個虛擬的設(shè)備命名為“hello”,而這個內(nèi)核驅(qū)動程序也命名為hello驅(qū)動程序。其實,Android內(nèi)核驅(qū)動程序和一般Linux內(nèi)核驅(qū)動程序的編寫方法是一樣的,都是以Linux模塊的形式實現(xiàn)的,具體可參考前面Android學(xué)習(xí)啟動篇一文中提到的Linux Device Drivers一書。不過,這里我們還是從Android系統(tǒng)的角度來描述Android內(nèi)核驅(qū)動程序的編寫和編譯過程。

       一. 參照前面兩篇文章Android源碼 在Ubuntu上下載,編譯和安裝和在Android內(nèi)核源碼 在Ubuntu上下載,編譯,安裝(Linux Kernel)準(zhǔn)備好Android內(nèi)核驅(qū)動程序開發(fā)環(huán)境。

       二. 進(jìn)入到kernel/common/drivers目錄,新建hello目錄:

             USER-NAME@MACHINE-NAME:~/Android$ cd kernel/common/drivers

             USER-NAME@MACHINE-NAME:~/Android/kernel/common/drivers$ mkdir hello

      三. 在hello目錄中增加hello.h文件:

#ifndef _HELLO_ANDROID_H_#define _HELLO_ANDROID_H_#include <linux/cdev.h>#include <linux/semaphore.h>#define HELLO_DEVICE_NODE_NAME "hello"#define HELLO_DEVICE_FILE_NAME "hello"#define HELLO_DEVICE_PROC_NAME "hello"#define HELLO_DEVICE_CLASS_NAME "hello"struct hello_android_dev {	int val;	struct semaphore sem;	struct cdev dev;};#endif

        這個頭文件定義了一些字符串常量宏,在后面我們要用到。此外,還定義了一個字符設(shè)備結(jié)構(gòu)體hello_android_dev,這個就是我們虛擬的硬件設(shè)備了,val成員變量就代表設(shè)備里面的寄存器,它的類型為int,sem成員變量是一個信號量,是用同步訪問寄存器val的,dev成員變量是一個內(nèi)嵌的字符設(shè)備,這個Linux驅(qū)動程序自定義字符設(shè)備結(jié)構(gòu)體的標(biāo)準(zhǔn)方法。

        四.在hello目錄中增加hello.c文件,這是驅(qū)動程序的實現(xiàn)部分

        驅(qū)動程序的功能主要是向上層提供訪問設(shè)備的寄存器的值,包括讀和寫。這里,提供了三種訪問設(shè)備寄存器的方法,一是通過proc文件系統(tǒng)來訪問,二是通過傳統(tǒng)的設(shè)備文件的方法來訪問,三是通過devfs文件系統(tǒng)來訪問。下面分段描述該驅(qū)動程序的實現(xiàn)。

         首先是包含必要的頭文件和定義三種訪問設(shè)備的方法:

#include <linux/init.h>#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/proc_fs.h>#include <linux/device.h>#include <asm/uaccess.h>#include "hello.h"/*主設(shè)備和從設(shè)備號變量*/static int hello_major = 0;static int hello_minor = 0;/*設(shè)備類別和設(shè)備變量*/static struct class* hello_class = NULL;static struct hello_android_dev* hello_dev = NULL;/*傳統(tǒng)的設(shè)備文件操作方法*/static int hello_open(struct inode* inode, struct file* filp);static int hello_release(struct inode* inode, struct file* filp);static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);/*設(shè)備文件操作方法表*/static struct file_operations hello_fops = {	.owner = THIS_MODULE,	.open = hello_open,	.release = hello_release,	.read = hello_read,	.write = hello_write, };/*訪問設(shè)置屬性方法*/static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf);static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);/*定義設(shè)備屬性*/static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store); 

   定義傳統(tǒng)的設(shè)備文件訪問方法,主要是定義hello_open、hello_release、hello_read和hello_write這四個打開、釋放、讀和寫設(shè)備文件的方法:

/*打開設(shè)備方法*/static int hello_open(struct inode* inode, struct file* filp) {	struct hello_android_dev* dev; 		/*將自定義設(shè)備結(jié)構(gòu)體保存在文件指針的私有數(shù)據(jù)域中,以便訪問設(shè)備時拿來用*/	dev = container_of(inode->i_cdev, struct hello_android_dev, dev);	filp->private_data = dev;		return 0;}/*設(shè)備文件釋放時調(diào)用,空實現(xiàn)*/static int hello_release(struct inode* inode, struct file* filp) {	return 0;}/*讀取設(shè)備的寄存器val的值*/static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {	ssize_t err = 0;	struct hello_android_dev* dev = filp->private_data; 	/*同步訪問*/	if(down_interruptible(&(dev->sem))) {		return -ERESTARTSYS;	}	if(count < sizeof(dev->val)) {		goto out;	} 	/*將寄存器val的值拷貝到用戶提供的緩沖區(qū)*/	if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {		err = -EFAULT;		goto out;	}	err = sizeof(dev->val);out:	up(&(dev->sem));	return err;}/*寫設(shè)備的寄存器值val*/static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {	struct hello_android_dev* dev = filp->private_data;	ssize_t err = 0; 	/*同步訪問*/	if(down_interruptible(&(dev->sem))) {		return -ERESTARTSYS; 	} 	if(count != sizeof(dev->val)) {		goto out; 	} 	/*將用戶提供的緩沖區(qū)的值寫到設(shè)備寄存器去*/	if(copy_from_user(&(dev->val), buf, count)) {		err = -EFAULT;		goto out;	}	err = sizeof(dev->val);out:	up(&(dev->sem));	return err;}

定義通過devfs文件系統(tǒng)訪問方法,這里把設(shè)備的寄存器val看成是設(shè)備的一個屬性,通過讀寫這個屬性來對設(shè)備進(jìn)行訪問,主要是實現(xiàn)hello_val_showhello_val_store兩個方法,同時定義了兩個內(nèi)部使用的訪問val值的方法__hello_get_val__hello_set_val:

/*讀取寄存器val的值到緩沖區(qū)buf中,內(nèi)部使用*/static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) {	int val = 0; 	/*同步訪問*/	if(down_interruptible(&(dev->sem))) { 		return -ERESTARTSYS; 	} 	val = dev->val; 	up(&(dev->sem)); 	return snprintf(buf, PAGE_SIZE, "%d/n", val);}/*把緩沖區(qū)buf的值寫到設(shè)備寄存器val中去,內(nèi)部使用*/static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) {	int val = 0; 	/*將字符串轉(zhuǎn)換成數(shù)字*/ 	val = simple_strtol(buf, NULL, 10); 	/*同步訪問*/ 	if(down_interruptible(&(dev->sem))) { 		return -ERESTARTSYS; 	} 	dev->val = val; 	up(&(dev->sem));	return count;}/*讀取設(shè)備屬性val*/static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {	struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); 	return __hello_get_val(hdev, buf);}/*寫設(shè)備屬性val*/static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { 	struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); 		return __hello_set_val(hdev, buf, count);}

  定義通過proc文件系統(tǒng)訪問方法,主要實現(xiàn)了hello_proc_readhello_proc_write兩個方法,同時定義了在proc文件系統(tǒng)創(chuàng)建和刪除文件的方法hello_create_prochello_remove_proc

/*讀取設(shè)備寄存器val的值,保存在page緩沖區(qū)中*/static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {	if(off > 0) {		*eof = 1;		return 0;	}	return __hello_get_val(hello_dev, page);}/*把緩沖區(qū)的值buff保存到設(shè)備寄存器val中去*/static ssize_t hello_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {	int err = 0;	char* page = NULL;	if(len > PAGE_SIZE) {		printk(KERN_ALERT"The buff is too large: %lu./n", len);		return -EFAULT;	}	page = (char*)__get_free_page(GFP_KERNEL);	if(!page) { 		printk(KERN_ALERT"Failed to alloc page./n");		return -ENOMEM;	} 	/*先把用戶提供的緩沖區(qū)值拷貝到內(nèi)核緩沖區(qū)中去*/	if(copy_from_user(page, buff, len)) {		printk(KERN_ALERT"Failed to copy buff from user./n"); 		err = -EFAULT;		goto out;	}	err = __hello_set_val(hello_dev, page, len);out:	free_page((unsigned long)page);	return err;}/*創(chuàng)建/proc/hello文件*/static void hello_create_proc(void) {	struct proc_dir_entry* entry;		entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);	if(entry) {		entry->owner = THIS_MODULE;		entry->read_proc = hello_proc_read;		entry->write_proc = hello_proc_write;	}}/*刪除/proc/hello文件*/static void hello_remove_proc(void) {	remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);}

 最后,定義模塊加載和卸載方法,這里只要是執(zhí)行設(shè)備注冊和初始化操作:

static int __hello_setup_dev(struct hello_android_dev* dev) {	int err;	dev_t devno = MKDEV(hello_major, hello_minor);	memset(dev, 0, sizeof(struct hello_android_dev));	cdev_init(&(dev->dev), &hello_fops);	dev->dev.owner = THIS_MODULE;	dev->dev.ops = &hello_fops; 	/*注冊字符設(shè)備*/	err = cdev_add(&(dev->dev),devno, 1);	if(err) {		return err;	} 	/*初始化信號量和寄存器val的值*/	init_MUTEX(&(dev->sem));	dev->val = 0;	return 0;}/*模塊加載方法*/static int __init hello_init(void){ 	int err = -1;	dev_t dev = 0;	struct device* temp = NULL;	printk(KERN_ALERT"Initializing hello device./n"); 	/*動態(tài)分配主設(shè)備和從設(shè)備號*/	err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);	if(err < 0) {		printk(KERN_ALERT"Failed to alloc char dev region./n");		goto fail;	}	hello_major = MAJOR(dev);	hello_minor = MINOR(dev); 	/*分配helo設(shè)備結(jié)構(gòu)體變量*/	hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);	if(!hello_dev) {		err = -ENOMEM;		printk(KERN_ALERT"Failed to alloc hello_dev./n");		goto unregister;	} 	/*初始化設(shè)備*/	err = __hello_setup_dev(hello_dev);	if(err) {		printk(KERN_ALERT"Failed to setup dev: %d./n", err);		goto cleanup;	} 	/*在/sys/class/目錄下創(chuàng)建設(shè)備類別目錄hello*/	hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);	if(IS_ERR(hello_class)) {		err = PTR_ERR(hello_class);		printk(KERN_ALERT"Failed to create hello class./n");		goto destroy_cdev;	} 	/*在/dev/目錄和/sys/class/hello目錄下分別創(chuàng)建設(shè)備文件hello*/	temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);	if(IS_ERR(temp)) {		err = PTR_ERR(temp);		printk(KERN_ALERT"Failed to create hello device.");		goto destroy_class;	} 	/*在/sys/class/hello/hello目錄下創(chuàng)建屬性文件val*/	err = device_create_file(temp, &dev_attr_val);	if(err < 0) {		printk(KERN_ALERT"Failed to create attribute val."); 		goto destroy_device;	}	dev_set_drvdata(temp, hello_dev); 	/*創(chuàng)建/proc/hello文件*/	hello_create_proc();	printk(KERN_ALERT"Succedded to initialize hello device./n");	return 0;destroy_device:	device_destroy(hello_class, dev);destroy_class:	class_destroy(hello_class);destroy_cdev:	cdev_del(&(hello_dev->dev));cleanup:	kfree(hello_dev);unregister:	unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);fail:	return err;}/*模塊卸載方法*/static void __exit hello_exit(void) {	dev_t devno = MKDEV(hello_major, hello_minor);	printk(KERN_ALERT"Destroy hello device./n"); 	/*刪除/proc/hello文件*/	hello_remove_proc(); 	/*銷毀設(shè)備類別和設(shè)備*/	if(hello_class) {		device_destroy(hello_class, MKDEV(hello_major, hello_minor));		class_destroy(hello_class);	} 	/*刪除字符設(shè)備和釋放設(shè)備內(nèi)存*/	if(hello_dev) {		cdev_del(&(hello_dev->dev));		kfree(hello_dev);	} 	/*釋放設(shè)備號*/	unregister_chrdev_region(devno, 1);}MODULE_LICENSE("GPL");MODULE_DESCRIPTION("First Android Driver");module_init(hello_init);module_exit(hello_exit);

       五.在hello目錄中新增Kconfig和Makefile兩個文件,其中Kconfig是在編譯前執(zhí)行配置命令make menuconfig時用到的,而Makefile是執(zhí)行編譯命令make是用到的:

       Kconfig文件的內(nèi)容

           config HELLO
           tristate "First Android Driver"
           default n
           help
           This is the first android driver.

       Makefile文件的內(nèi)容     

       obj-$(CONFIG_HELLO) += hello.o

      在Kconfig文件中,tristate表示編譯選項HELLO支持在編譯內(nèi)核時,hello模塊支持以模塊、內(nèi)建和不編譯三種編譯方法,默認(rèn)是不編譯,因此,在編譯內(nèi)核前,我們還需要執(zhí)行make menuconfig命令來配置編譯選項,使得hello可以以模塊或者內(nèi)建的方法進(jìn)行編譯。

      在Makefile文件中,根據(jù)選項HELLO的值,執(zhí)行不同的編譯方法。

      六. 修改arch/arm/Kconfig和drivers/kconfig兩個文件,在menu "Device Drivers"和endmenu之間添加一行: 

           source "drivers/hello/Kconfig"   

       這樣,執(zhí)行make menuconfig時,就可以配置hello模塊的編譯選項了。

      七. 修改drivers/Makefile文件,添加一行:    

           obj-$(CONFIG_HELLO) += hello/

       八. 配置編譯選項:

        USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make menuconfig     

        找到"Device Drivers" => "First Android Drivers"選項,設(shè)置為y。       

注意,如果內(nèi)核不支持動態(tài)加載模塊,這里不能選擇m,雖然我們在Kconfig文件中配置了HELLO選項為tristate。要支持動態(tài)加載模塊選項,必須要在配置菜單中選擇Enable loadable module support選項;在支持動態(tài)卸載模塊選項,必須要在Enable loadable module support菜單項中,選擇Module unloading選項。

       九. 編譯:     

      USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make       

      編譯成功后,就可以在hello目錄下看到hello.o文件了,這時候編譯出來的zImage已經(jīng)包含了hello驅(qū)動。    

      十. 參照在Ubuntu上下載、編譯和安裝Android最新內(nèi)核源代碼(Linux Kernel)一文所示,運行新編譯的內(nèi)核文件,驗證hello驅(qū)動程序是否已經(jīng)正常安裝:      

        USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &
        USER-NAME@MACHINE-NAME:~/Android$ adb shell      

        進(jìn)入到dev目錄,可以看到hello設(shè)備文件:      

        root@android:/ # cd dev
        root@android:/dev # ls      

        進(jìn)入到proc目錄,可以看到hello文件:   

        root@android:/ # cd proc
        root@android:/proc # ls    

        訪問hello文件的值:    

        root@android:/proc # cat hello
        0

        root@android:/proc # echo '5' > hello
        root@android:/proc # cat hello
        5

        進(jìn)入到sys/class目錄,可以看到hello目錄:

        root@android:/ # cd sys/class
        root@android:/sys/class # ls

        進(jìn)入到hello目錄,可以看到hello目錄:

        root@android:/sys/class # cd hello
        root@android:/sys/class/hello # ls

        進(jìn)入到下一層hello目錄,可以看到val文件:

        root@android:/sys/class/hello # cd hello
        root@android:/sys/class/hello/hello # ls

        訪問屬性文件val的值:

        root@android:/sys/class/hello/hello # cat val
        5

        root@android:/sys/class/hello/hello # echo '0'  > val
        root@android:/sys/class/hello/hello # cat val
        0    

  至此,我們的hello內(nèi)核驅(qū)動程序就完成了,并且驗證一切正常。這里我們采用的是系統(tǒng)提供的方法和驅(qū)動程序進(jìn)行交互,也就是通過proc文件系統(tǒng)和devfs文件系統(tǒng)的方法,下一篇文章中,我們將通過自己編譯的C語言程序來訪問/dev/hello文件來和hello驅(qū)動程序交互,敬請期待。

 后續(xù)繼續(xù)整理相關(guān)文章資料,希望能幫助研究Android源碼的朋友,謝謝大家支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 雅江县| 湖北省| 乌海市| 芦山县| 金坛市| 博爱县| 铜鼓县| 大埔区| 大埔县| 子洲县| 改则县| 八宿县| 普宁市| 连城县| 九江市| 时尚| 蒲城县| 原阳县| 怀安县| 双柏县| 夏河县| 广德县| 特克斯县| 隆子县| 谢通门县| 银川市| 黄骅市| 禄劝| 宝坻区| 大厂| 济源市| 谷城县| 阿图什市| 龙里县| 平果县| 彭水| 长春市| 偃师市| 鄄城县| 南江县| 弋阳县|