乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      MTK Android添加驅(qū)動模塊

       dwlinux_gs 2014-07-17










      [編寫linux驅(qū)動程序]



      一、編寫驅(qū)動核心程序



      這里說的驅(qū)動核心程序是指運行在內(nèi)核空間的,完全按linux驅(qū)動格式編寫的,基本上與android沒什么關(guān)系,一般包括xxx.h和xxx.c文件。



      進入到kernel/drivers目錄,新建snsled目錄,然后建立對應(yīng)的snsled.h和snsled.c文件:



      //snsled.h


      #ifndef _SNSLED_H_
      #define _SNSLED_H_
      
      #define SNSLED_NUM (1)
      
      #define SNSLED_CLASS_NAME  "snsled"
      #define SNSLED_DEVICE_NAME	"snsled"
      #define SNSLED_NODE_NAME   "snsled"
      #define SNSLED_PROC_NAME	"snsled"
      
      #define SNSLED_IOC_MAGIC   'k'
      
      #define SNSLED_IO_ON        2323	//_IO(SNSLED_IOC_MAGIC, 0)
      #define SNSLED_IO_OFF       2324	//_IO(SNSLED_IOC_MAGIC, 1)
      #define SNSLED_IOW_PWM     	2325	//_IOW(SNSLED_IOC_MAGIC, 2, int)
      #define SNSLED_IOR_PWM     	2326	//_IOR(SNSLED_IOC_MAGIC, 3, int)
      
      struct snsled_cntx {
          int r1;
          struct semaphore sem; 
      	struct cdev cdev;
      };
      
      #endif


      //snsled.c


      #include <linux/module.h>
      #include <linux/moduleparam.h>
      #include <linux/init.h>
      
      #include <linux/kernel.h>	/* printk() */
      #include <linux/slab.h>		/* kmalloc() */
      #include <linux/fs.h>		/* everything... */
      #include <linux/errno.h>	/* error codes */
      #include <linux/types.h>	/* size_t */
      #include <linux/proc_fs.h>
      #include <linux/fcntl.h>	/* O_ACCMODE */
      #include <linux/seq_file.h>
      #include <linux/cdev.h>
      
      #include <asm/system.h>		/* cli(), *_flags */
      #include <asm/uaccess.h>	/* copy_*_user */
      
      //#include <asm/semaphore.h> /* semaphore */
      #include <linux/semaphore.h>  
      #include <linux/device.h>   /*class_create*/  
      
      #include "snsled.h"		/* local definitions */
      
      
      /*
      #include <linux/module.h>
      #include <linux/platform_device.h>
      #include <linux/delay.h>
      #include <linux/string.h>
      #include <linux/ctype.h>
      #include <linux/leds.h>
      #include <linux/leds-mt65xx.h>
      #include <linux/workqueue.h>
      #include <linux/wakelock.h>
      #include <linux/slab.h>
      
      #include <cust_leds.h>*/
      
      #if defined (CONFIG_ARCH_MT6573)
      #include <mach/mt6573_pwm.h>
      #include <mach/mt6573_gpio.h>
      #include <mach/pmu6573_sw.h>
      
      #elif defined (CONFIG_ARCH_MT6516)
      #include <mach/mt6516_pwm.h>
      #include <mach/mt6516_gpio.h>
      
      #endif
      
      
      /*====macros====*/
      #define BUF_SIZE (64)
      
      #define SNS_LED_CONTROL_LINE				GPIO99	//GPIO39
      #define SNS_LED_CONTROL_LINE_GPIO_MODE		GPIO_MODE_00	
      #define SNS_LED_CONTROL_LINE_PWM_MODE		GPIO_MODE_01
      
      
      
      /*====declares====*/
      ssize_t snsled_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
      ssize_t snsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
      long snsled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
      int snsled_open(struct inode *inode, struct file *filp);
      int snsled_release(struct inode *inode, struct file *filp);
      
      int snsled_turn_on(void);
      int snsled_turn_off(void);
      int snsled_set_pwm(int arg);
      
      
      /*====global====*/
      static int g_snsled_major = 0;
      static int g_snsled_minor = 0;
      
      struct snsled_cntx *g_snsled_ptr = NULL;
      
      struct class *g_snsled_class = 0;
      
      static struct file_operations g_snsled_fops = {
          .owner = THIS_MODULE,
          .read = snsled_read,
          .write = snsled_write, 
          .unlocked_ioctl = snsled_unlocked_ioctl,
          .open = snsled_open,
          .release = snsled_release,
      };
      
      
      /*====implements====*/
      ssize_t snsled_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
      #if 1
          printk(KERN_ALERT "Snsled read.\n");
      #else
          char tmp_buf[512] = {0};
          int len =sprintf(tmp_buf, "snsled read.\n");
          if (copy_to_user(buf, tmp_buf, count)) {
              //do nothing
      	}
      #endif
          return 0;
      }
      
      ssize_t snsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
      	printk(KERN_ALERT "Snsled write.\n");
          return count;
      }
      
      long snsled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
          int err = 0;
          long retval = 0;
      	
      	printk(KERN_ALERT "Snsled ioctl:cmd=%d, arg=%d.\n", cmd, arg);
      	
      	/*
      	//extract the type and number bitfields, and don't decode
      	//wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
      	if (_IOC_TYPE(cmd) != SNSLED_IOC_MAGIC) return -ENOTTY;
      	//if (_IOC_NR(cmd) > SNSLED_IOC_MAXNR) return -ENOTTY;
      
      	//to verify *arg is in user space
      	if (_IOC_DIR(cmd) & _IOC_READ)
      		err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
      	else if (_IOC_DIR(cmd) & _IOC_WRITE)
      		err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
      	if (err) return -EFAULT;
             */
          switch(cmd)
          {
              case SNSLED_IO_ON:
                  printk(KERN_ALERT "Snsled ioctl:on.\n");
      			//if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ONE)){printk("Snsled set gpio failed!! \n");}
      			snsled_turn_on();
                  break;
      			
              case SNSLED_IO_OFF:
                  printk(KERN_ALERT "Snsled ioctl:off.\n");
      			//if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}
      			snsled_turn_off();
                  break;
      			
              case SNSLED_IOW_PWM:            
      			printk(KERN_ALERT "Snsled ioctl:set pwm, arg=%d.\n", arg);
                  //retval = __get_user(g_snsled_ptr->r1, (int __user *)arg);
                  snsled_set_pwm((int __user *)arg);
                  break;
      
              case SNSLED_IOR_PWM:   
      			#if 0
                  retval = __put_user(g_snsled_ptr->r1, (int __user *)arg);
                  printk(KERN_ALERT "Snsled ioctl:read r1:%i.\n", g_snsled_ptr->r1);
      			#endif
      			printk(KERN_ALERT "Snsled ioctl:read pwm -- not configured yet.\n");
                  break;
      
              default:
                  printk(KERN_ALERT "Snsled ioctl:you got the wrong command.\n");
                  break;
          }
          
          return retval;
      }
      
      int snsled_open(struct inode *inode, struct file *filp) {
      	printk(KERN_ALERT "Snsled: snsled_open.\n");
      #if 0	
          if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}
          if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}
          if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ONE)){printk("Snsled set gpio failed!! \n");}
      #endif	
          return 0;
      }
      
      int snsled_release(struct inode *inode, struct file *filp) {
      	printk(KERN_ALERT "Snsled: snsled_release.\n");
      #if 0	
          if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}
          if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}
          if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}
      #endif	
          return 0;
      }
      
      int snsled_turn_on(void) {
      	printk(KERN_ALERT "Snsled: snsled_turn_on.\n");
          if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_PWM_MODE)){printk("Snsled set gpio mode failed!! \n");}
          if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}
          if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}
      	return 0;
      }
      
      int snsled_turn_off(void) {
      	printk(KERN_ALERT "Snsled: snsled_turn_off.\n");
          if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}
          if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}
          if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}
      	return 0;
      }
      
      //for old mode
      /**
      struct _PWM_OLDMODE_REGS {
      	U16 IDLE_VALUE; //0
      	U16 GUARD_VALUE;	//0
      	U16 GDURATION;	//~
      	U16 WAVE_NUM;	//0
      	U16 DATA_WIDTH;	//high level, 13bits, 0~8191
      	U16 THRESH;	//t
      }PWM_MODE_OLD_REGS;
      **/
      int snsled_set_pwm(int arg) {
      	struct pwm_spec_config pwm_setting;
      	pwm_setting.pwm_no = PWM1;
      	printk(KERN_ALERT "Snsled: snsled_open begin.\n");
      
      	
      	pwm_setting.mode = PWM_MODE_OLD;
      	pwm_setting.clk_div = CLK_DIV16;//CLK_DIV128;
      	pwm_setting.clk_src = PWM_CLK_OLD_MODE_32K;	
      	pwm_setting.PWM_MODE_OLD_REGS.IDLE_VALUE = 0;
      	pwm_setting.PWM_MODE_OLD_REGS.GUARD_VALUE = 0;
      	pwm_setting.PWM_MODE_OLD_REGS.GDURATION = 8100;
      	pwm_setting.PWM_MODE_OLD_REGS.WAVE_NUM = 0;
      	pwm_setting.PWM_MODE_OLD_REGS.DATA_WIDTH = 8100;
      	pwm_setting.PWM_MODE_OLD_REGS.THRESH = 8100;
      
      	pwm_set_spec_config(&pwm_setting);	
      	printk(KERN_ALERT "Snsled: snsled_open done.\n");
      
      	return 0;
      }
      
      
      
      
      //alloc device major
      static int vircdex_alloc_major(void) {
          dev_t devt = 0;
          int result = 0;
          
          result = alloc_chrdev_region(&devt, g_snsled_minor, SNSLED_NUM, SNSLED_NODE_NAME);
          g_snsled_major = MAJOR(devt);
          
          return result;
      }
      static int snsled_release_major(void) {
          dev_t devt = MKDEV(g_snsled_major, g_snsled_minor);
          unregister_chrdev_region(devt, 1);
          return 0;
      }
      
      static int snsled_setup_dev(struct snsled_cntx *dev) {
          int err, devno = MKDEV(g_snsled_major, g_snsled_minor);
          
          cdev_init(&(dev->cdev), &g_snsled_fops);
          dev->cdev.owner = THIS_MODULE;
          err = cdev_add(&dev->cdev, devno, 1);
          if(err){   
              return err;
          }
          
          //init_MUTEX(&(dev->sem));
          sema_init(&(dev->sem), 1);
          
          return 0;
      }
      static int snsled_unsetup_dev(struct snsled_cntx *dev) {
          cdev_del(&(dev->cdev));
          return 0;
      }
      
      static int snsled_create_devfiles(dev_t devt) {//, const struct device_attribute *attr) {
          int err = -1;    
          struct device *dev = NULL;
                                
          g_snsled_class = class_create(THIS_MODULE, SNSLED_CLASS_NAME);
          if(IS_ERR(g_snsled_class)) {  
              err = PTR_ERR(g_snsled_class);  
              printk(KERN_ALERT "Failed to create class.\n");  
              goto CLASS_CREATE_ERR;  
          }
          
          dev = device_create(g_snsled_class, NULL, devt, NULL, SNSLED_DEVICE_NAME);
          //dev = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);  
          //device_create( my_class, NULL, MKDEV(hello_major, 0), "hello" "%d", 0 );
          //dev = device_create(g_snsled_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME);
          if(IS_ERR(dev)) {  
              err = PTR_ERR(dev);  
              printk(KERN_ALERT "Failed to create device.\n");  
              goto DEVICE_CREATE_ERR;  
          }
          
          /*err = device_create_file(dev, attr);  
          if(err < 0) {  
              printk(KERN_ALERT"Failed to create attribute file.");                  
              goto DEVICE_CREATE_FILE_ERR;  
          }*/          
          printk(KERN_ALERT "seems ok.\n"); //zmk@@debug    
          
          return 0;
          
      DEVICE_CREATE_FILE_ERR:
          device_destroy(g_snsled_class, devt);
      DEVICE_CREATE_ERR:
          class_destroy(g_snsled_class);             
      CLASS_CREATE_ERR:  
          return err; 
      }
      static int snsled_delete_devfiles(dev_t devt) {
          device_destroy(g_snsled_class, devt);
          class_destroy(g_snsled_class);
          //device_remove_file(dev, attr);
          return 0;
      }
      
      static int snsled_read_proc(char *buf, char **start, off_t offset,
                                  int count, int *eof, void *data)
      {
          int len =sprintf(buf, "snsled read proc.\n");
          return len;
      }
      static int snsled_create_proc_file(void) {
          struct proc_dir_entry *entry = NULL;
       
          entry = create_proc_read_entry(SNSLED_PROC_NAME, 0,
                                  NULL, snsled_read_proc,
                                  NULL);
          if(entry)
          {
              return 0;
          }
          else                            
          {                        
              return -1;                       
          }                       
      }
      static int snsled_delete_proc_file(void) {
          remove_proc_entry(SNSLED_PROC_NAME, NULL);
          return 0;
      }
      
      MODULE_LICENSE("GPL");
      
      static int snsled_init(void) {
          int err = -1;
          dev_t devt = 0;
          
          //[1] alloc node number
          err = vircdex_alloc_major();
          if(0 > err)
          {
              printk(KERN_ALERT"alloc major failed.\n");
              goto ALLOC_MAJOR_ERR;
          }
          devt = MKDEV(g_snsled_major, g_snsled_minor);
          
          //[2] device object init    
          g_snsled_ptr = kmalloc(sizeof(struct snsled_cntx), GFP_KERNEL);  
          if(!g_snsled_ptr) {  
              err = -ENOMEM;  
              printk(KERN_ALERT"kmalloc failed.\n");  
              goto KMALLOC_ERR;  
          }
          memset(g_snsled_ptr, 0, sizeof(struct snsled_cntx));
          
          //[3] setup device
          err = snsled_setup_dev(g_snsled_ptr);
          if(0 > err)
          {
              printk(KERN_ALERT"device setup failed.\n");
              goto DEVICE_SETUP_ERR;
          }
          
          //[4] create files in directory "/dev/" and "/sys/" 
          ///err = snsled_create_devfiles(devt, attr);
          err = snsled_create_devfiles(devt);
          if(0 > err)
          {
              printk(KERN_ALERT"devfiles create failed.\n");
              goto DEVFILES_CREATE_ERR;
          }
      
          //[5] create proc file
          err = snsled_create_proc_file();
          if(0 > err)
          {
              printk(KERN_ALERT"proc file create failed.\n");
              goto PROC_FILE_CREATE_ERR;
          }
              
          return 0;
          
      PROC_FILE_CREATE_ERR:
          snsled_delete_devfiles(devt);
      DEVFILES_CREATE_ERR:
          snsled_unsetup_dev(g_snsled_ptr);
      DEVICE_SETUP_ERR:
          kfree(g_snsled_ptr); 
      ALLOC_MAJOR_ERR:
          snsled_release_major();
      KMALLOC_ERR:
          return err;
      }
      static void snsled_exit(void) {
          dev_t devt = MKDEV(g_snsled_major, g_snsled_minor);
          
          snsled_delete_proc_file();
          snsled_delete_devfiles(devt);
          snsled_unsetup_dev(g_snsled_ptr);
          kfree(g_snsled_ptr); 
          snsled_release_major();
      }
      
      module_init(snsled_init);
      module_exit(snsled_exit);


      二、配置Kconfig



      在snsled目錄中,新建Kconfig文件:


             config SNSLED
                 tristate "snsled Driver"
                 default n #y ?
                 help
                 This is the sns led driver.
      


      其中,tristate表示編譯選項HELLO支持在編譯內(nèi)核時,hello模塊支持以模塊、內(nèi)建和不編譯三種編譯方法。



      三、配置Makefile



      1、在snsled目錄中,新建snsled文件夾對應(yīng)的Makefile:


      #obj-$(CONFIG_SNSLED) += snsled.o
      obj-y += snsled.o
      


      上面根據(jù) CONFIG_SNSLED的值確定是否編譯,y為編譯。



      2、修改snsled的父目錄 drivers/下的Makefile,加入:


      obj-$(CONFIG_HELLO) += snsled/
      


      這樣便能在編譯時編譯到snsled這個文件夾。



      四、配置系統(tǒng)的autoconfig



      打開 mediatek/config/bbk73_gb/autoconfig/kconfig/project,加入:


      CONFIG_SNSLED=y
      


      在這里定義變量 CONFIG_SNSLED.



      • ps:目前這里配置好像還不能snsled自動編譯進去,目前的操作是直接在用到CONFIG_SNSLED的地方用y替代。


      五、編譯


      ./makeMtk bbk73_gb remake kernel bootimage
      






      [編寫hal模塊]



      一、新建xxx.h文件



      進入"hardware/libhardware/include/hardware"目錄,新建vircdev.h文件:


      #ifndef ANDROID_HELLO_INTERFACE_H
      #define ANDROID_HELLO_INTERFACE_H
      #include <hardware/hardware.h>
      
      __BEGIN_DECLS
      
      /*定義模塊ID*/
      #define HELLO_HARDWARE_MODULE_ID "hello"
      
      /*硬件模塊結(jié)構(gòu)體*/
      struct hello_module_t {
      	struct hw_module_t common;
      };
      
      /*硬件接口結(jié)構(gòu)體*/
      struct hello_device_t {
      	struct hw_device_t common;
      	int fd;
      	int (*set_val)(struct hello_device_t* dev, int val);
      	int (*get_val)(struct hello_device_t* dev, int* val);
      };
      
      __END_DECLS
      
      #endif


      這里按照Android硬件抽象層規(guī)范的要求,分別定義模塊ID、模塊結(jié)構(gòu)體hello_module_t以及硬件接口結(jié)構(gòu)體hello_device_t。在硬件接口結(jié)構(gòu)體中,fd表示設(shè)備文件描述符,對應(yīng)linux下我們經(jīng)常接觸到的設(shè)備文件"/dev/xxx",set_val和get_val為該HAL對上提供的函數(shù)接口。







      二、新建xxx.c文件



      進入到hardware/libhardware/modules目錄,新建hello目錄,并添加hello.c文件。 hello.c的內(nèi)容較多,我們分段來看。



      1、包含相關(guān)頭文件和定義相關(guān)結(jié)構(gòu)


      #define LOG_TAG "HelloStub"
      
      #include <hardware/hardware.h>
      #include <hardware/hello.h>
      #include <fcntl.h>
      #include <errno.h>
      #include <cutils/log.h>
      #include <cutils/atomic.h>
      
      #define DEVICE_NAME "/dev/hello"
      #define MODULE_NAME "Hello"
      #define MODULE_AUTHOR "shyluo@gmail.com"
      
      /*設(shè)備打開和關(guān)閉接口*/
      static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
      static int hello_device_close(struct hw_device_t* device);
      
      /*設(shè)備訪問接口*/
      static int hello_set_val(struct hello_device_t* dev, int val);
      static int hello_get_val(struct hello_device_t* dev, int* val);
      
      /*模塊方法表*/
      static struct hw_module_methods_t hello_module_methods = {
      	open: hello_device_open
      };
      
      /*模塊實例變量*/
      struct hello_module_t HAL_MODULE_INFO_SYM = {
      	common: {
      		tag: HARDWARE_MODULE_TAG,
      		version_major: 1,
      		version_minor: 0,
      		id: HELLO_HARDWARE_MODULE_ID,
      		name: MODULE_NAME,
      		author: MODULE_AUTHOR,
      		methods: &hello_module_methods,
      	}
      };


      這里,實例變量名必須為HAL_MODULE_INFO_SYM,tag也必須為HARDWARE_MODULE_TAG,這是Android硬件抽象層規(guī)范規(guī)定的。



      2、定義hello_device_open函數(shù)


      static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
      	struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));
      	
      	if(!dev) {
      		LOGE("Hello Stub: failed to alloc space");
      		return -EFAULT;
      	}
      
      	memset(dev, 0, sizeof(struct hello_device_t));
      	dev->common.tag = HARDWARE_DEVICE_TAG;
      	dev->common.version = 0;
      	dev->common.module = (hw_module_t*)module;
      	dev->common.close = hello_device_close;
      	dev->set_val = hello_set_val;dev->get_val = hello_get_val;
      
      	if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
      		LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev);
      		return -EFAULT;
      	}
      
      	*device = &(dev->common);
      	LOGI("Hello Stub: open /dev/hello successfully.");
      
      	return 0;
      }


      DEVICE_NAME定義為"/dev/hello"。由于設(shè)備文件是在內(nèi)核驅(qū)動里面通過device_create創(chuàng)建的,而device_create創(chuàng)建的設(shè)備文件默認只有root用戶可讀寫,而hello_device_open一般是由上層APP來調(diào)用的,這些APP一般不具有root權(quán)限,這時候就導(dǎo)致打開設(shè)備文件失?。?/p>

      Hello Stub: failed to open /dev/hello -- Permission denied.
      


      解決辦法是類似于Linux的udev規(guī)則,打開Android源代碼工程目錄下,進入到system/core/rootdir目錄,里面有一個名為ueventd.rc文件,往里面添加一行:


      /dev/hello 0666 root root
      


      3、定義自定義的api函數(shù)



      這里定義hello_device_close、hello_set_val和hello_get_val這三個函數(shù):


      static int hello_device_close(struct hw_device_t* device) {
      	struct hello_device_t* hello_device = (struct hello_device_t*)device;
      
      	if(hello_device) {
      		close(hello_device->fd);
      		free(hello_device);
      	}
      	
      	return 0;
      }
      
      static int hello_set_val(struct hello_device_t* dev, int val) {
      	LOGI("Hello Stub: set value %d to device.", val);
      
      	write(dev->fd, &val, sizeof(val));
      
      	return 0;
      }
      
      static int hello_get_val(struct hello_device_t* dev, int* val) {
      	if(!val) {
      		LOGE("Hello Stub: error val pointer");
      		return -EFAULT;
      	}
      
      	read(dev->fd, val, sizeof(*val));
      
      	LOGI("Hello Stub: get value %d from device", *val);
      
      	return 0;
      }


      三、在hello目錄下新建Android.mk文件


      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE_TAGS := optional
      LOCAL_PRELINK_MODULE := false
      LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
      LOCAL_SHARED_LIBRARIES := liblog
      LOCAL_SRC_FILES := hello.c
      LOCAL_MODULE := hello.default
      include $(BUILD_SHARED_LIBRARY)


      注意,LOCAL_MODULE的定義規(guī)則,hello后面跟有default,hello.default能夠保證我們的模塊總能被硬象抽象層加載到。



      四、編譯、重新打包Android系統(tǒng)鏡像system.img


      $:~/Android$ mmm hardware/libhardware/modules/hello
      


      編譯成功后,就可以在out/target/product/generic/system/lib/hw目錄下看到hello.default.so文件了。


      $:USER-NAME@MACHINE-NAME:~/Android$ make snod
      


      重新打包后,system.img就包含我們定義的硬件抽象層模塊hello.default了。







      [編寫jni]



      雖然上一節(jié)我們在Android系統(tǒng)為我們自己的硬件增加了一個硬件抽象層模塊,但是現(xiàn)在Java應(yīng)用程序還不能訪問到我們的硬件。我們還必須編寫JNI方法和在Android的Application Frameworks層增加API接口,才能讓上層Application訪問我們的硬件。在這一節(jié)中,我們將首先完成jni接口的編寫。







      一、新建com_android_server_HelloService.cpp文件



      進入到frameworks/base/services/jni目錄,新建com_android_server_HelloService.cpp文件:


      #define LOG_TAG "HelloService"  
      #include "jni.h"  
      #include "JNIHelp.h"  
      #include "android_runtime/AndroidRuntime.h"  
      #include <utils/misc.h>  
      #include <utils/Log.h>  
      #include <hardware/hardware.h>  
      #include <hardware/hello.h>  
      #include <stdio.h> 
      
      namespace android  
      {  
          /*在硬件抽象層中定義的硬件訪問結(jié)構(gòu)體,參考<hardware/hello.h>*/  
          struct hello_device_t* hello_device = NULL;  
      
          /*通過硬件抽象層定義的硬件訪問接口設(shè)置硬件寄存器val的值*/  
          static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {  
              int val = value;  
              LOGI("Hello JNI: set value %d to device.", val);  
              if(!hello_device) {  
                  LOGI("Hello JNI: device is not open.");  
                  return;  
              }  
                
              hello_device->set_val(hello_device, val);  
          }  
      
          /*通過硬件抽象層定義的硬件訪問接口讀取硬件寄存器val的值*/  
          static jint hello_getVal(JNIEnv* env, jobject clazz) {  
              int val = 0;  
              if(!hello_device) {  
                  LOGI("Hello JNI: device is not open.");  
                  return val;  
              }  
              hello_device->get_val(hello_device, &val);  
                
              LOGI("Hello JNI: get value %d from device.", val);  
            
              return val;  
          }  
      
          /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設(shè)備*/  
          static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {  
              return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
          }  
      
          /*通過硬件模塊ID來加載指定的硬件抽象層模塊并打開硬件*/  
          static jboolean hello_init(JNIEnv* env, jclass clazz) {  
              hello_module_t* module;  
                
              LOGI("Hello JNI: initializing......");  
              if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
                  LOGI("Hello JNI: hello Stub found.");  
                  if(hello_device_open(&(module->common), &hello_device) == 0) {  
                      LOGI("Hello JNI: hello device is open.");  
                      return 0;  
                  }  
                  LOGE("Hello JNI: failed to open hello device.");  
                  return -1;  
              }  
              LOGE("Hello JNI: failed to get hello stub module.");  
              return -1;        
          }  
      
          /*JNI方法表*/  
          static const JNINativeMethod method_table[] = {  
              {"init_native", "()Z", (void*)hello_init},  
              {"setVal_native", "(I)V", (void*)hello_setVal},  
              {"getVal_native", "()I", (void*)hello_getVal},  
          };  
      
          /*注冊JNI方法*/  
          int register_android_server_HelloService(JNIEnv *env) {  
                  return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));  
          }  
      };


      注意文件的命名方法,com_android_server前綴表示的是包名,表示硬件服務(wù)HelloService是放在frameworks/base/services/java目錄下的com/android/server目錄的,即存在一個命名為com.android.server.HelloService的類。這里,我們暫時略去HelloService類的描述,在下一篇文章中,我們將回到HelloService類來。簡單地說,HelloService是一個提供Java接口的硬件訪問服務(wù)類。



      在這個cpp文件中,我們主要是做了以下事情:



      1、包括頭文件



      (尤其是在hal層所定義的頭文件)


      #define LOG_TAG "HelloService"  
      #include "jni.h"  
      #include "JNIHelp.h"  
      #include "android_runtime/AndroidRuntime.h"  
      #include <utils/misc.h>  
      #include <utils/Log.h>  
      #include <hardware/hardware.h>  
      #include <hardware/hello.h>  
      #include <stdio.h>


      2、編寫jni接口



      通過對hal中函數(shù)的調(diào)用,編寫jni接口(這里只是簡單地進行了一層包裝):



      • 注意,linux driver -- hal -- jni, jni與linux driver并無直接關(guān)系,即jni的函數(shù)接口與linux driver不一定完全一一對應(yīng),很簡單的一個例子便是在linux driver中可能只有一個ioctl函數(shù),可是在hal層卻通過對ioctl的調(diào)用實現(xiàn)了get,set,exchange等多個功能.
        /*通過硬件抽象層定義的硬件訪問接口設(shè)置硬件寄存器val的值*/  
        static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {  
            int val = value;  
            LOGI("Hello JNI: set value %d to device.", val);  
            if(!hello_device) {  
                LOGI("Hello JNI: device is not open.");  
                return;  
            }  
                  
            hello_device->set_val(hello_device, val);  
        }  
        
        /*通過硬件抽象層定義的硬件訪問接口讀取硬件寄存器val的值*/  
        static jint hello_getVal(JNIEnv* env, jobject clazz) {  
            int val = 0;  
            if(!hello_device) {  
                LOGI("Hello JNI: device is not open.");  
                return val;  
            }  
            hello_device->get_val(hello_device, &val);  
                  
            LOGI("Hello JNI: get value %d from device.", val);  
              
            return val;  
        }  



      3、定義jni加載函數(shù),注冊jni方法表


      /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設(shè)備*/  
      static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {  
          return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
      }  
      
      /*通過硬件模塊ID來加載指定的硬件抽象層模塊并打開硬件*/  
      static jboolean hello_init(JNIEnv* env, jclass clazz) {  
          hello_module_t* module;  
                
          LOGI("Hello JNI: initializing......");  
          if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
              LOGI("Hello JNI: hello Stub found.");  
              if(hello_device_open(&(module->common), &hello_device) == 0) {  
                  LOGI("Hello JNI: hello device is open.");  
                  return 0;  
              }  
              LOGE("Hello JNI: failed to open hello device.");  
              return -1;  
          }  
          LOGE("Hello JNI: failed to get hello stub module.");  
          return -1;        
      }  
      
      /*JNI方法表*/  
      static const JNINativeMethod method_table[] = {  
          {"init_native", "()Z", (void*)hello_init},  
          {"setVal_native", "(I)V", (void*)hello_setVal},  
          {"getVal_native", "()I", (void*)hello_getVal},  
      };  
      
      /*注冊JNI方法*/  
      int register_android_server_HelloService(JNIEnv *env) {  
              return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));  
      }  


      其中,上面示例中的jni加載函數(shù)hello_init是通過hal中定義的hello_device_open函數(shù)實現(xiàn)的。在hello_init函數(shù)中,通過Android硬件抽象層提供的hw_get_module方法來加載模塊ID為HELLO_HARDWARE_MODULE_ID的硬件抽象層模塊,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定義的。Android硬件抽象層會根據(jù)HELLO_HARDWARE_MODULE_ID的值在Android系統(tǒng)的/system/lib/hw目錄中找到相應(yīng)的模塊,然后加載起來,并且返回hw_module_t接口給調(diào)用者使用。在jniRegisterNativeMethods函數(shù)中,第二個參數(shù)的值必須對應(yīng)HelloService所在的包的路徑,即com.android.server.HelloService。







      二、修改onload.cpp,使系統(tǒng)啟動時自動加載JNI方法調(diào)用表



      修改frameworks/base/services/jni/onload.cpp:



      1、在namespace android增加register_android_server_HelloService函數(shù)聲明:


      namespace android {
      //......
      int register_android_server_HelloService(JNIEnv *env);
      };


      2、在JNI_onLoad增加register_android_server_HelloService函數(shù)調(diào)用:


      extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
      {
      //......
      register_android_server_HelloService(env);
      //......
      }


      這樣,在Android系統(tǒng)初始化時,就會自動加載該JNI方法調(diào)用表。







      三、修改Android.mk文件,添加編譯路徑



      打開frameworks/base/services/jni/Android.mk,在LOCAL_SRC_FILES變量中增加一行:


      LOCAL_SRC_FILES:=     com_android_server_AlarmManagerService.cpp     com_android_server_BatteryService.cpp     com_android_server_InputManager.cpp     com_android_server_LightsService.cpp     com_android_server_PowerManagerService.cpp     com_android_server_SystemServer.cpp     com_android_server_UsbService.cpp     com_android_server_VibratorService.cpp     com_android_server_location_GpsLocationProvider.cpp     com_android_server_HelloService.cpp /
          onload.cpp






      四、編譯和重新生成system.img


      $:mmm frameworks/base/services/jni
      $:make snod
      


      這樣,重新打包的system.img鏡像文件就包含我們剛才編寫的JNI方法了







      [編寫Framework接口]



      在Android系統(tǒng)中,硬件服務(wù)一般是運行在一個獨立的進程中為各種應(yīng)用程序提供服務(wù)。因此,調(diào)用這些硬件服務(wù)的應(yīng)用程序與這些硬件服務(wù)之間的通信需要通過代理來進行。



      一、定義通信接口



      1、新增接口文件



      進入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl接口定義文件:


      package android.os;  
             
      interface IHelloService {  
          void setVal(int val);  
          int getVal();  
      }  


      IHelloService接口主要提供了設(shè)備和獲取硬件寄存器val的值的功能,分別通過setVal和getVal兩個函數(shù)來實現(xiàn)。



      2、添加編譯路徑



      返回到frameworks/base目錄,打開Android.mk文件,修改LOCAL_SRC_FILES變量的值,增加IHelloService.aidl源文件:


      LOCAL_SRC_FILES += /
         //......
         core/java/android/os/IVibratorService.aidl /
         core/java/android/os/IHelloService.aidl /
         core/java/android/service/urlrenderer/IUrlRendererService.aidl /
         //.....


      3、編譯接口文件


      $:mmm frameworks/base
      


      這樣,就會根據(jù)IHelloService.aidl生成相應(yīng)的IHelloService.Stub接口。







      二、建立java文件,編寫Framework接口



      進入到frameworks/base/services/java/com/android/server目錄,新增HelloService.java文件:


      package com.android.server;
      import android.content.Context;
      import android.os.IHelloService;
      import android.util.Slog;
      public class HelloService extends IHelloService.Stub {
      	private static final String TAG = "HelloService";
      	HelloService() {
      		init_native();
      	}
      	public void setVal(int val) {
      		setVal_native(val);
      	}	
      	public int getVal() {
      		return getVal_native();
      	}
      	
      	private static native boolean init_native();
          	private static native void setVal_native(int val);
      	private static native int getVal_native();
      };






      三、在ServerThread::run函數(shù)中增加加載代碼



      修改同目錄的SystemServer.java文件:


      @Override
           public void run() {
           //.....
                  try {
                        Slog.i(TAG, "DiskStats Service");
                        ServiceManager.addService("diskstats", new DiskStatsService(context));
                  } catch (Throwable e) {
                        Slog.e(TAG, "Failure starting DiskStats Service", e);
                  }
      
                  //start:增加加載代碼
                  try {
                        Slog.i(TAG, "Hello Service");
                        ServiceManager.addService("hello", new HelloService());
                  } catch (Throwable e) {
                        Slog.e(TAG, "Failure starting Hello Service", e);
                  }
                  //end
           //......
           }      






      四、編譯、重新打包system.img


      $:mmm frameworks/base/services/java
      $:make snod
      


      這樣,重新打包后的system.img系統(tǒng)鏡像文件就在Application Frameworks層中包含了我們自定義的硬件服務(wù)了,并且會在系統(tǒng)啟動的時候會自動加載HelloService,這樣應(yīng)用程序就可以通過Java接口來訪問Hello硬件服務(wù)了。







      [App訪問]


      //...
      import android.os.IHelloService;
      //...
      private IHelloService helloService = null;
      //...
          @Override
          public void onCreate(Bundle savedInstanceState) {
      //...
      	helloService = IHelloService.Stub.asInterface(
      		ServiceManager.getService("hello"));
      //...
          }
      //...
      int val = helloService.getVal();
      //...
      helloService.setVal(val);
      //...






      參考資料:



      在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內(nèi)核驅(qū)動程序



      在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務(wù)接口







      http://www./en/%E6%B7%BB%E5%8A%A0%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9D%97



        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多