embeddev_1 发表于 2015-6-6 20:35:51

转贴一个使用cdev开发的linux led 驱动程序

本帖最后由 embeddev_1 于 2015-6-6 20:39 编辑

这篇文章写得非常好,特地转来,希望有识之士在此基础上再完成移植到android 系统下的led驱动,网络上找的一些android led驱动多少都有些问题{:biggrin:}

2.6内核以后的大量驱动代码, 有许多字符驱动不使用之前代码的方法. 那是还没有更新到 2.6 内核接口的老代码. 因为那个代码实际上能用,这个更新可能很长时间不会发生. 为完整, 我们描述老的字符设备注册接口, 但是新代码不应当使用它; 这个机制在将来内核中可能会消失{:biggrin:}

/*
* tq2440_leds.c
*
*Created on: 2011-11-29
*      Author: liufei_learning
*
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kdev_t.h>

#define DEVICE_NAME "tq2440_leds"    //设备名称
#define LED_MAJOR   231                //主设备号
#define IOCTL_LED_ON    1               //LED亮状态
#define IOCTL_LED_OFF   0                //LED灭状态

static led_major = LED_MAJOR;
struct cdev led_cdev;   
//控制LED的IO口
static unsigned long led_table[] =
{
    S3C2410_GPB5,
    S3C2410_GPB6,
    S3C2410_GPB7,
    S3C2410_GPB8,
};

//LED IO口的模式
static unsigned int led_cfg_table[] =
{
    S3C2410_GPB5_OUTP,
    S3C2410_GPB6_OUTP,
    S3C2410_GPB7_OUTP,
    S3C2410_GPB8_OUTP,
};

static struct class *leds_class;

static int __init leds_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int __init leds_ioctl(struct inode *inode, struct file *file,
      unsigned int cmd, unsigned long arg)
{
    //检测是第几个LED,因开发板上只有4个,索引从0开始
    if(arg < 0 || arg > 3)
    {
      return -EINVAL;
    }

    //判断LED要执行哪种状态
    switch(cmd)
    {
      case IOCTL_LED_ON:
      {
            s3c2410_gpio_setpin(led_table, ~(IOCTL_LED_ON));
            break;
      }
      case IOCTL_LED_OFF:
      {
            s3c2410_gpio_setpin(led_table, ~(IOCTL_LED_OFF));
            break;
      }
      default:
      {
            return -EINVAL;
      }
    }

    return 0;
}

static struct file_operations led_fops =
{
    .owner = THIS_MODULE,
    .open = leds_open,
    .ioctl = leds_ioctl,
};

static int __init leds_init(void)
{
    int ret;
    int i;
    for(i = 0; i < 4; i++)
    {
      //初始化各IO口为输出模式
      s3c2410_gpio_cfgpin(led_table, led_cfg_table);
      //由原理图可知LED电路是共阳极的(即各IO口输出低电平0才会点亮)
      //这里初始化为1,不让LED点亮
      s3c2410_gpio_setpin(led_table, ~(IOCTL_LED_OFF));
    }


<span style="color:#ff0000;">dev_t devno = MKDEV(led_major, 0);
    /* 静态申请设备号*/
    if (led_major)
      ret = register_chrdev_region(devno, 2, DEVICE_NAME);
    else /* 动态分配设备号 */
    {
      ret = alloc_chrdev_region(&devno, 0, 2, DEVICE_NAME);
      led_major = MAJOR(devno);
    }   
    if (ret < 0)
      return ret;
      
    /*初始化cdev结构*/
    cdev_init(&led_cdev, &led_fops);
    led_cdev.owner = THIS_MODULE;
    led_cdev.ops = &led_fops;
   
    /* 注册字符设备 */
    cdev_add(&led_cdev, MKDEV(led_major, 0), 1);</span>
      

    //注册一个类,使mdev可以在/dev/下面建立设备节点
    leds_class = class_create(THIS_MODULE, DEVICE_NAME);
    if( IS_ERR(leds_class) )
    {
      printk("creat leds_class failed!");
      return -1;
    }

    //创建一个设备节点,节点名字为DEVICE_NAME
    device_create(leds_class, NULL, MKDEV(led_major, 0), NULL, DEVICE_NAME);
    printk(DEVICE_NAME "initialized!");
    return 0;
}

static void __init leds_exit(void)
{
    cdev_del(&led_cdev); /*注销设备*/
    unregister_chrdev_region(MKDEV(led_major, 0), 2); /*释放设备号*/
    //删除设备节点
    device_destroy(leds_class, MKDEV(led_major, 0));
    //注销类
    class_destroy(leds_class);
}

module_init( leds_init);
module_exit( leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liufei_learning");

MODULE_DESCRIPTION("tq2440 leds driver");

embeddev_1 发表于 2015-6-6 20:37:32

在贴之前的老式linuxled驱动代码
/*
* tq2440_leds.c
*
* Created on: 2011-11-29
*   Author: liufei_learning
*
*/
   
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<mach/hardware.h>
#include<mach/regs-gpio.h>
#include<linux/device.h>
   
#defineDEVICE_NAME      "tq2440_leds"    //设备名称
#defineLED_MAJOR      231                //主设备号
#defineIOCTL_LED_ON      1               //LED亮状态
#defineIOCTL_LED_OFF      0                //LED灭状态
   
//控制LED的IO口
static unsigned longled_table[] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
   
//LED IO口的模式
static unsigned intled_cfg_table[] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
   
static struct class*leds_class;
   
static int __initleds_open(struct inode *inode, struct file *file)
{
return0;
}
   
static int __initleds_ioctl(struct inode *inode, struct file *file,
unsignedint cmd, unsigned long arg)
{
//检测是第几个LED,因开发板上只有4个,索引从0开始
if(arg< 0 || arg > 3)
{
return-EINVAL;
}
   
//判断LED要执行哪种状态
switch(cmd)
{
caseIOCTL_LED_ON:
{
s3c2410_gpio_setpin(led_table,~(IOCTL_LED_ON));
break;
}
caseIOCTL_LED_OFF:
{
s3c2410_gpio_setpin(led_table,~(IOCTL_LED_OFF));
break;
}
default:
{
return-EINVAL;
}
}
   
return0;
}
   
static structfile_operations led_fops =
{
.owner= THIS_MODULE,
.open= leds_open,
.ioctl= leds_ioctl,
};
   
static int __initleds_init(void)
{
intret;
int i;
for(i= 0; i < 4; i++)
{
//初始化各IO口为输出模式
s3c2410_gpio_cfgpin(led_table,led_cfg_table);
//由原理图可知LED电路是共阳极的(即各IO口输出低电平0才会点亮)
//这里初始化为1,不让LED点亮
s3c2410_gpio_setpin(led_table,~(IOCTL_LED_OFF));
}
   
//注册LED设备为字符设备
ret =register_chrdev(LED_MAJOR, DEVICE_NAME, &led_fops);
   
if(ret< 0)
{
printk(DEVICE_NAME" major number register falid!\n");
returnret;
}
   
//注册一个类,使mdev可以在/dev/下面建立设备节点
leds_class= class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(leds_class) )
{
printk("creatleds_class failed!");
return-1;
}
   
//创建一个设备节点,节点名字为DEVICE_NAME
device_create(leds_class,NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);
printk(DEVICE_NAME"initialized!");
return0;
}
   
static void __initleds_exit(void)
{
//注销设备
unregister_chrdev(LED_MAJOR,DEVICE_NAME);
//删除设备节点
device_destroy(leds_class,MKDEV(LED_MAJOR, 0));
//注销类
class_destroy(leds_class);
}
   
module_init( leds_init);
module_exit( leds_exit);
   
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liufei_learning");
MODULE_DESCRIPTION("tq2440leds driver");

embeddev_1 发表于 2015-6-6 20:38:02

应用程序 {:biggrin:}

/*
==============================================
Name         : led_test.c
Author         : liufei_learning
Date         : 11/27/2011
Description    : tq2440 led driver test
==============================================
*/
   
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include<sys/ioctl.h>
   
int main(int argc, char**argv)
{
    int turn, index, fd;
   
    //检测输入的参数合法性
    if(argc != 3 || sscanf(argv,"%d", &index) != 1 || index < 1 || index > 4)
    {
      printf("Usage: led_test on|off1|2|3|4\n");
      exit(1);
    }
   
    if(strcmp(argv, "on") == 0)
    {
      turn = 1;
    }
    else if(strcmp(argv, "off") ==0)
    {
      turn = 0;
    }
    else
    {
      printf("Usage: led_test on|off1|2|3|4\n");
      exit(1);
    }
   
    //打开LED设备
    fd = open("/dev/tq2440_leds", 0);
   
    if(fd < 0)
    {
      printf("Open Led DeviceFaild!\n");
      exit(1);
    }
   
    //IO控制
    ioctl(fd, turn, index - 1);
   
    //关闭LED设备
    close(fd);
   
    return 0;
}

embeddev_1 发表于 2015-6-6 20:40:44

编译到内核并运行的步骤{:biggrin:}

把LED驱动代码部署到内核中去
#cp -f tq2440_leds.c/linux-2.6.30.4/drivers/char    //把驱动源码复制到内核驱动的字符设备下

#vim /linux-2.6.30.4/drivers/char/Kconfig    //添加LED设备配置
view plaincopyprint?
config TQ2440_LEDS
   tristate "TQ2440 Leds Device"
   depends on ARCH_S3C2440
   default y
   ---help---
      TQ2440User Leds

#vim /linux-2.6.30.4/drivers/char/Makefile    //添加LED设备配置
obj-$(CONFIG_TQ2440_LEDS) +=tq2440_leds.o
5. 配置内核,选择LED设备选项
#make menuconfig
Device Drivers --->
    Character devices --->
      <*> TQ2440 Leds Device (NEW)
6.编译内核make zImage
7.编译测试程序#arm-linux-gcc -o led_test led_test.c 将生成的文件复制到开发板 /usr/sbin下
8.开发板上测试
查看已加载的设备:#cat/proc/devices,可以看到tq2440_leds的主设备号为231
控制led输入led_test on 1可以看到对应led被点亮

embeddev_1 发表于 2015-6-6 20:45:00

再转个android 下led的驱动,分4步,这个多少是有点问题

1: 内核硬件驱动层
2: HAL(硬件抽象层)
3: 框架层(framework,包含JNI和实现硬件服务的JAVA接口)
4: JAVA应用层

一,首先是第一层,内核硬件驱动层
    在kernel\driver\下新建一个目录swt,在swt下再新建led文件夹,并新建swtled.c swtled.h,通过注册模块方式,实现ioctl,write,read接口。之后通过设备名给上层访问。
    swtled.c部分
   
点击(此处)折叠或打开
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>

#include <asm/io.h>
#include <mach/gpio.h>
#include <asm/uaccess.h>


#define LED_ON _IO ('k',1)
#define LED_OFF _IO ('k',2)


struct light_dev
{
    struct cdev cdev;
   
    unsigned char value;
};

static struct light_dev *light_devp;
static struct class * light_class = NULL;


int light_major = 250;

#if 0


static ssize_t light_val_show(struct device* dev, struct device_attribute* attr, char* buf)
{
    return 0;
}

static ssize_t light_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
    return 0;
}


static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, light_val_show, light_val_store);

#endif

void led_off(int arg)
{
    switch(arg)
    {
      case 1: gpio_set_value(RK29_PIN6_PB3,GPIO_LOW); //GPIO8
      break;

      case 2: gpio_set_value(RK29_PIN6_PC2,GPIO_LOW); //GPIO10
      break;

      default:
      break;
    }
}

void led_on(int arg)
{
    switch(arg)
    {
      case 1: gpio_set_value(RK29_PIN6_PB3,GPIO_HIGH);
      break;

      case 2: gpio_set_value(RK29_PIN6_PC2,GPIO_HIGH);
      break;

      default:
      break;
    }
}


int light_open(struct inode *inode, struct file *filp)
{
    struct light_dev *dev;

    int ret = 0;

    printk(KERN_ALERT"light_open()");
   

    dev = container_of(inode->i_cdev, struct light_dev, cdev);

    filp->private_data = dev;

    ret = gpio_request(RK29_PIN6_PB3, NULL);

      if(ret != 0)
      {
            gpio_free(RK29_PIN6_PB3);
      }
      gpio_direction_output(RK29_PIN6_PB3, 0);

      ret = gpio_request(RK29_PIN6_PC2, NULL);

      if(ret != 0)
      {
            gpio_free(RK29_PIN6_PC2);
      }
      gpio_direction_output(RK29_PIN6_PC2, 0);



    return 0;
}

int light_release(struct inode *inode, struct file *filp)
{
    printk(KERN_ALERT"light_release()");
   
    return 0;
}



ssize_t light_read(struct file *filp, char __user *buf, size_t count, loff_t*f_pos)
{
    struct light_dev *dev = filp->private_data;

    printk(KERN_ALERT"light_read()");

    if (copy_to_user(buf, &(dev->value), 1))
    {
      return -EFAULT;
    }
   
    return 1;
}

ssize_t light_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct light_dev *dev = filp->private_data;

    printk(KERN_ALERT"light_write()");
   
    if (copy_from_user(&(dev->value), buf, 1))
    {
      return -EFAULT;
    }
   


    if (dev->value == 1)
      printk(KERN_ALERT"LED on");
    else
      printk(KERN_ALERT"LED off");
    return 1;
}


int light_ioctl(struct inode    *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct light_dev *dev = filp->private_data;

    printk(KERN_ALERT"light_ioctl(cmd = %d)", cmd);
   
    switch (cmd)
      {
            case LED_ON:
                led_on(arg);
                break;
            case LED_OFF:
                led_off(arg);
                break;
            default: break;
      }

   
    return 0;
}

struct file_operations light_fops =
{
    .owner = THIS_MODULE,
    .read = light_read,
    .write = light_write,
    .ioctl = light_ioctl,
    .open = light_open,
    .release = light_release,
};

static int light_setup_cdev(struct light_dev *dev, int index)
{
    int err, devno = MKDEV(light_major, index);
    cdev_init(&dev->cdev, &light_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &light_fops;
    dev->value = 0;
    err = cdev_add(&dev->cdev, devno, 1);

    return err;
}



int light_init(void)
{
    int result = 0;
   
    struct device * temp = NULL;   

    dev_t dev = MKDEV(light_major, 0);
      
    printk(KERN_ALERT"light_init()");


   
    if (light_major) {
      result = register_chrdev_region(dev, 1, "LED");
    }
    else {
      result = alloc_chrdev_region(&dev, 0, 1, "LED");

      light_major = MAJOR(dev);
    }
   
    if (result < 0) {
      goto failed;
    }
   

   
    light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL);   
    if (!light_devp){   
      result = -ENOMEM;      
      goto unregister;
    }
   
    memset(light_devp, 0, sizeof(struct light_dev));
   
    result = light_setup_cdev(light_devp, 0);
    if(result) {
      goto cleanup;
    }


   
    light_class = class_create(THIS_MODULE, "led");
    if(IS_ERR(light_class)) {
      printk(KERN_ALERT"Failed to create light class./n");
      goto destroy_cdev;   
    }         
   

   
    temp = device_create(light_class, NULL, dev, "%s", "swtled");
    if(IS_ERR(temp)) {   
      printk(KERN_ALERT"Failed to create hello device.");
      goto destroy_class;   
    }

#if 0

   
    result = device_create_file(temp, &dev_attr_val);
    if(result < 0) {
   printk(KERN_ALERT"Failed to create attribute val.");
   goto destroy_device;
    }

    dev_set_drvdata(temp, light_devp);
#endif   
   

   
    return 0;

destroy_device:
    device_destroy(light_class, dev);

destroy_class:   
    class_destroy(light_class);

destroy_cdev:
    cdev_del(&(light_devp->cdev));

cleanup:
      kfree(light_devp);
      
unregister:

    unregister_chrdev_region(dev, 1);
   
failed:

    printk(KERN_ALERT"light_init failed.");
   
   
return result;

}


void light_cleanup(void)
{
    printk(KERN_ALERT"light_cleanup()");


   
    if(light_class) {
      device_destroy(light_class, MKDEV(light_major, 0));
      class_destroy(light_class);
    }


    if(light_devp) {
      cdev_del(&light_devp->cdev);
      kfree(light_devp);
    }
   
    unregister_chrdev_region(MKDEV(light_major, 0), 1);
   
}

module_init(light_init);
module_exit(light_cleanup);


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("led driver");
MODULE_ALIAS("led module");
led文件夹中Kconfig文件

点击(此处)折叠或打开
config SWT_XXX_LED
    bool "swt led"
    default n
    help
   This compiles in support for the led of xxx.
led文件夹中MakeFile文件


点击(此处)折叠或打开
obj-$(CONFIG_SWT_XXX_LED)+=swt_led.o
swtled文件中Kconfig文件

点击(此处)折叠或打开
menu "swtled Drivers"

source "drivers/swt/led/Kconfig"

endmenu
swtled文件夹中MakeFile文件

点击(此处)折叠或打开
obj-y += led/
接下来,就要修改kernel/driver下的MakeFile和Kconfig文件了,在分别其中添加一句

MakeFile文件
点击(此处)折叠或打开
obj-y += swt/
Kconfig文件

点击(此处)折叠或打开
source "drivers/swt/Kconfig"
保存所有之后,执行make
编译成功后,就可以在swt/led目录下看到swtled.o文件了,这时候编译出来的img已经包含了swtled驱动了

embeddev_1 发表于 2015-6-6 20:46:00

二,硬件抽象层

1,在hardware\libhardware\include\hardware下新建swtled.h文件
swtled.h
点击(此处)折叠或打开
#ifndef ANDROID_SWTLED_INTERFACE_H
#define ANDROID_SWTLED_INTERFACE_H

#include <hardware/hardware.h>

__BEGIN_DECLS

//module interface
struct swtled_module_t
{
    struct hw_module_t common;
};

//device interface
struct swtled_device_t
{
    struct hw_device_t common;
    int fd;
    int (*set_on)(struct swtled_device_t* dev,int number);
    int (*set_off)(struct swtled_device_t* dev,int number);
};


#define SWTLED_HARDWARE_MODULE_ID "swtled"
__END_DECLS

#endif

2:在hardware/libhardware/module下新建swtled文件夹并新建swtled.c

点击(此处)折叠或打开
#define LOG_TAG "swtledStub"

#include <hardware/hardware.h>
#include <hardware/swtled.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>


#define LED_ON _IO('k',1)
#define LED_OFF _IO('k',2)

#define DEVICE_NAME "/dev/swt_led"
#define MODULE_NAME "swtled"
#define MODULE_AUTHOR "zc"



//open or close device interface

static int swtled_device_open(const struct hw_module_t* module,const char* name, struct hw_device_t** device);

static int swtled_device_close(struct hw_device_t* device);


//light-on light-off
static int swtled_set_on(struct swtled_device_t* dev,int number);
static int swtled_set_off(struct swtled_device_t* dev,int number);


static int swtled_device_open(const struct hw_module_t* module,const char* name, struct hw_device_t** device)
{

    struct swtled_device_t * dev;
    dev = (struct swtled_device_t*)malloc(sizeof(struct swtled_device_t));
    if (!dev)
    {
   LOGE("Swtled Stub:failed to alloc space");
      return -EFAULT;
            
    }
    memset(dev,0,sizeof(struct swtled_device_t));
    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (hw_module_t*)module;
    dev->common.close = swtled_device_close;
    dev->set_on = swtled_set_on;
    dev->set_off = swtled_set_off;

    if ((dev->fd = open(DEVICE_NAME,O_RDWR)) == -1)
    {
      LOGE("Swtled Stub:failed to open /dev/swt_led -- %s.",strerror(errno));
      free(dev);
      return -EFAULT;
    }

    *device = &(dev->common);
    LOGI("Swtled Stub: open /dev/swt_led successfully");

    return 0;
}

static int swtled_device_close(struct hw_device_t* device)
{
    struct swtled_device_t* swtled_device = (struct swtled_device_t*)device;
    if (swtled_device)
    {
      close(swtled_device->fd);
      free(swtled_device);
    }
    return 0;
}


//------------------------------------------------------------------------------
//number: 1: light one 2:light two
//------------------------------------------------------------------------------
static int swtled_set_on(struct swtled_device_t* device,int number)
{
    ioctl(device->fd,LED_ON,number);
    return 0;
}


//------------------------------------------------------------------------------
//number: 1: light one 2:light two
//------------------------------------------------------------------------------
static int swtled_set_off(struct swtled_device_t* device,int number)
{
    ioctl(device->fd,LED_OFF,number);
    return 0;
}



//module method table
static struct hw_module_methods_t swtled_module_methods =
{
    open: swtled_device_open
};



const struct swtled_module_t HAL_MODULE_INFO_SYM = {

    common:
    {
      tag: HARDWARE_MODULE_TAG,
      version_major: 1,
      version_minor: 0,
      id: SWTLED_HARDWARE_MODULE_ID,
      name:MODULE_NAME,
      author: MODULE_AUTHOR,
      methods: &swtled_module_methods,      
    }
      
};
这里主要是通过hw_module_t和hw_device_t结构体形式将内核中led的驱动封装一层,
导出set_on和set_off接口,到时供JNI层的CPP调用。由于权限问题,应用层在打开dev时可能提示permission deny,所以需要修改
system\core\rootdir下的ueventd.rc文件,在其中添加一句

点击(此处)折叠或打开
/dev/swt_led 0666 root root
3,在同目录下添加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 := swtled.c
LOCAL_MODULE := swtled.default
include $(BUILD_SHARED_LIBRARY)
4,编译
(1 进入到gingerbread目录下,执行mmm hardware/libhardware/modules/swtled会
    编译成功后,就可以在out/target/product/generic/system/lib/hw目录下看到swtled.default.so文件了

(2 再执行make snod,使system.img镜像文件中包名硬件抽象模块swtled.default.so

embeddev_1 发表于 2015-6-6 20:46:35

三,框架层(framework)
   这里主要实现两部分
(1: 为Android HAL编写JNI方法,以便使得上层的APP能够使用下层提供的硬件服务
(2: 在Android系统的框架层提供Java接口的硬件服务
1,在frameworks/base/services/jni中新建com_android_server_swtled.cpp
点击(此处)折叠或打开
#define LOG_TAG "SwtledService"
#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/swtled.h>
#include <stdio.h>



namespace android
{
    struct swtled_device_t* swtled_device = NULL;


    static void swtled_set_on(JNIEnv* env, jobject clazz,jint number)
    {
      int num = number;
      LOGI("Swtled JNI: select lighton is %d",num);

      if (!swtled_device)
      {
            LOGI("Swtled JNI:device is not open.");
            return;
      }
      swtled_device->set_on(swtled_device,num);
    }

    static void swtled_set_off(JNIEnv* env, jobject clazz,jint number)
    {
         
      int num = number;
      LOGI("Swtled JNI: select lightoff is %d",num);

      if (!swtled_device)
      {
            LOGI("Swtled JNI:device is not open.");
            return;
      }
      swtled_device->set_off(swtled_device,num);
    }

    static inline int swtled_device_open(hw_module_t* module,swtled_device_t** device)
    {
      return module->methods->open(module,SWTLED_HARDWARE_MODULE_ID,(hw_device_t**)device);
    }
   

    static jboolean swtled_init(JNIEnv* env,jclass clazz)
    {
      swtled_module_t* swtledmodule;
      
      LOGI("Swtled JNI: initializing...");

      if (hw_get_module(SWTLED_HARDWARE_MODULE_ID,(const struct hw_module_t**)&swtledmodule)==0)
      {
            LOGI("Swtled JNI: swtled stub be found.");
            if (swtled_device_open(&(swtledmodule->common),&swtled_device) == 0)
            {
                LOGI("Swtled JNI: swtled device open successful.");
                return 0;
            }

            LOGI("Swtled JNI: failed to open swtled device.");
            return -1;
      }
      
      LOGI("Swtled JNI: failed to get swtled stub module.");
      return -1;
   }
   
   static const JNINativeMethod method_table[] =
   {
      {"init_native", "()Z", (void*)swtled_init},
      {"setOn_native","(I)V",(void*)swtled_set_on},
      {"setOff_native","(I)V",(void*)swtled_set_off},
   };

    int register_android_server_SwtledService(JNIEnv* env)
    {
      return jniRegisterNativeMethods(env,"com/android/server/SwtledService",method_table,NELEM(method_table));
    }
}
修改同级目录下的Android.mk和Onload.cpp文件
在Android.mk的LOCAL_SRC_FILES:=下添加
点击(此处)折叠或打开
LOCAL_SRC_FILES:= \
....
com_android_server_swtled.cpp \
在Onload.cpp中的namespace android {下加入
点击(此处)折叠或打开
int register_android_server_SwtledService(JNIEnv* env);
同时在JNI_OnLoad函数下加入
点击(此处)折叠或打开
register_android_server_SwtledService(env);

2,编译
   (1,执行mmm framework/base/services/jni
   (2,make snod
3,在frameworks/base/core/java/android/os 新建ISwtledService.aidl,代码如下
点击(此处)折叠或打开
package android.os;



interface ISwtledService {

    void setOn(int number);

    void setOff(int number);

}
返回到frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加 ISwtledService.aidl源文件

点击(此处)折叠或打开
LOCAL_SRC_FILES += /
....................................................................
core/java/android/os/IVibratorService.aidl /
core/java/android/os/ISwtledService.aidl /
core/java/android/service/urlrenderer/IUrlRendererService.aidl /
4,编译ISwtledService接口
mmm framework/base,如果正确,那么会根据ISwtledService.aidl生成对应的ISwtledService.Stub接口

5,进入到frameworks/base/services/java/com/android/server目录,新增ISwtledService.java文件:


点击(此处)折叠或打开
package com.android.server;
import android.content.Context;
import android.os.ISwtledService;
import android.util.Slog;


public class SwtledService extends ISwtledService.Stub {

    private static final String TAG = "SwtledService";

    SwtledService()
    {
      init_native();
    }

    public void setOn(int number)
    {
      setOn_native(number);
    }

    public void setOff(int number)
    {
      setOff_native(number);
    }

    private static native boolean init_native();
    private static native void setOn_native(int number);
    private static native void setOff_native(int number);
};
6,修改同目录下的SystemServer.java文件,在ServerThread::run方法里加入

点击(此处)折叠或打开
try{
               
                Slog.i(TAG, "Swtled Service");
                ServiceManager.addService("swtled", new SwtledService());
            } catch (Throwable e) {
                Slog.e(TAG, "Failure starting Swtled Service", e);
            }
7,编译SwtledService.java并打包进system.img
   (1)mmm framework/base/services/java
   (2)make snod

embeddev_1 发表于 2015-6-6 20:47:09

四,应用层(APP)

1,为了方便,使用Eclipse编写一个简单的控制灯的应用,添加四个button,分配控制灯1亮,灯1灭,灯2亮,灯2灭,Activity代码如下

点击(此处)折叠或打开
package com.android.swtled;

import android.app.Activity;
import android.os.Bundle;
import android.os.ISwtledService;
import android.os.ServiceManager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.os.RemoteException;

public class swtled extends Activity {
   
    private static final String LOG_TAG = "com.android.swtled";
    private ISwtledService swtledService = null;
   
    private Button setOnLight1Button = null;
    private Button setOffLight1Button = null;
    private Button setOnLight2Button = null;
    private Button setOffLight2Button = null;
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      swtledService = ISwtledService.Stub.asInterface(ServiceManager.getService("swtled"));

      setOnLight1Button = (Button)findViewById(R.id.button_seton_light1);
      setOffLight1Button = (Button)findViewById(R.id.button_setoff_light1);
      setOnLight2Button = (Button)findViewById(R.id.button_seton_light2);
      setOffLight2Button = (Button)findViewById(R.id.button_setoff_light2);

      setOnLight1Button.setOnClickListener(new OnClickListener()
      {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                try{
                  
                  swtledService.setOn(1);
                }catch(RemoteException e)
                {
                  Log.e(LOG_TAG, "Exception while setOn Light.");

                }
            }
            
      });
      
      setOffLight1Button.setOnClickListener(new OnClickListener()
      {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                try{
                  
                  swtledService.setOff(1);
                }catch(RemoteException e)
                {
                  Log.e(LOG_TAG, "Exception while setOn Light.");

                }
            }
            
      });
      
      setOnLight2Button.setOnClickListener(new OnClickListener()
      {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                try{
                  
                  swtledService.setOn(2);
                }catch(RemoteException e)
                {
                  Log.e(LOG_TAG, "Exception while setOn Light.");

                }
            }
            
      });
      
      setOffLight2Button.setOnClickListener(new OnClickListener()
      {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                try{
                  
                  swtledService.setOff(2);
                }catch(RemoteException e)
                {
                  Log.e(LOG_TAG, "Exception while setOn Light.");

                }
            }
      });
    }
}
2, AndroidManifest.xml脚本

点击(此处)折叠或打开
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.android.swtled"
      android:versionCode="1"
      android:versionName="1.0">


    <application android:icon="@drawable/icon" android:label="@string/app_name">
      <activity android:name=".swtled"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
      </activity>   
    </application>
</manifest>

3,字符串文件res/values/strings.xml

点击(此处)折叠或打开
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, swtled!</STRING>
    <string name="app_name">swtled</string>
    <string name="seton_light1"></string>
    <string name="setoff_light1"></string>
    <string name="seton_light2"></string>
    <string name="setoff_light2"></string>
</resources>
4,布局文件脚本main.xml

点击(此处)折叠或打开
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
    <LinearLayout
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:orientation="horizontal"
          android:gravity="center">
   
   <Button
         android:id="@+id/button_seton_light1"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:text = "@string/seton_light1"
   />
   
   <Button
   android:id = "@+id/button_setoff_light1"
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:text = "@string/setoff_light1"
   />
   
   <Button
         android:id="@+id/button_seton_light2"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:text = "@string/seton_light2"
   />
   
   <Button
   android:id = "@+id/button_setoff_light2"
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:text = "@string/setoff_light2"
   />
   </LinearLayout>
</LinearLayout>
5,由于是编译器是Eclipse,但实际编译却是要在android源代码环境下编译,于是,将工程目录的三个文件
src,res以及AndroidManifest.xml复制到gingerbread源码目录的package/experimental/swtled下,其中
swtled文件是需要自己新建的,复制完成之后,进入编译阶段

6,编译app并生成apk文件
(1) mmm package/experimental/swtled
编译成功后,便可以在out/target/product/generic/system/app目录下看到swtled.apk文件
(2) 重新打包系统镜像文件system.img
   make snod

7,开机测试


8,注意事项:
在工程中需要import两个包是
import android.os.ISwtledService;
import android.os.ServiceManager;

程序通过ServiceManager.getService("swtled")来获得SwtledService,
接着通过ISwtledService.Stub.asInterface函数转换为ISwtledService接口。
其中,服务名字“swtled”是系统启动时加载SwtledService时指定的,
而ISwtledService接口定义在android.os.ISwtledService中
需要注意的是,虽然用Eclipse编译这个应用,但是无法用Eclipse编译,最后还是需要将工程中res,src以及AndroidManifest.xml三个文件copy到gingerbread/package/experimental/swtled,swtled是自己再新建的文件夹, 然后使用 mmm package/experimental/swtled编译,如果正确,最后会生成.apk文件。

embeddev_1 发表于 2015-6-6 20:50:46

和android下的驱动编写作对比.linux下就是个喳呀!{:lol:}

lyg407 发表于 2015-6-6 20:53:02

不明觉厉。

wszyjsw2 发表于 2015-6-6 21:26:01

碉堡了            
页: [1]
查看完整版本: 转贴一个使用cdev开发的linux led 驱动程序