android分层学习笔记(五)

来源:互联网 发布:淘宝网男童保暖内衣 编辑:程序博客网 时间:2024/05/22 10:29

在android系统中,以上几个小节文章中,把它的分层做了一些简单的描述,接下就是内核驱动相关的内容。这是一个自由的世界,当然很复杂。也正是因为自由,才可以构建不同的操作系统世界,android是其中之一。不管android是不是传统意义上的linux上的操作系统,它毕竟实现了一些很有用的东西。

     对于内核这里不想详细展开,毕竟自己的功底也有限。单说一些简单的驱动。事实上,自己所接触的驱动颇为简单,无外乎管脚的控制。下面以一个简单char驱动来说明android底层的东西,其实就是linux驱动内容:

 

对于驱动开发,那么肯定是要看“Linux设备驱动程序第三版2.6”(LDDP)这本书了。目前已经是2.6版本了,有中文版本。

       看完这本书,再看以下例子,你会觉得非常简单。不看那本书,按照以下例子,当然也可以写一个简单的驱动程序,需要注意的下面的例子是字符驱动。

       linux下驱动有一个框架,一般来说完成以下几个函数就可以了。

xxx_read(struct file *filp, char __user*buff, size_t count, loff_t *offp)

xxx_write(struct file *filp, const char__user *buff, size_t count, loff_t *offp)

xxx_open(struct inode *inode, struct file*filp)

xxx_release(struct inode *inode, structfile *filp)

xxx_ioctl(struct inode *inode, struct file*file,

                                   unsignedint cmd, unsigned long arg)

static int __init xxx_init(void)

static void __exit xxx_exit(void)

 

module_init(xxx_init);

module_exit(xxx_exit);

 

       实际应用中,字符驱动write/read函数很少用到。一般也可以不实现,可以由ioctl函数来实现。

       驱动与应用程序接口函数对应,看驱动函数的名字就知道了。

       xxx_read  <---> read

xxx_write  <---> write

xxx_open  <---> open

xxx_release  <---> close

xxx_ioctl  <---> ioctl

       也就是说,在应用程序调用read,write,open,ioctl函数,最终就是调用其驱动的对应的函数。

       insmod时,会调用xxx_init

       rmmod时,会调用xxx_exit

 

对于xxx_init函数,那么要实现的东西比较多,比如io口的设置,或其他变量的初始赋值,驱动的注册,设备节点的创建等。相应地,xxx_exit就完成xxx_init所做事下结逆过程。此处详说:

每个驱动都有一个主设备号和次设备号,可以固定设置,但不能有冲突。最好的方法是系统分配,这也是LDDP作者所建议的。

static structfile_operations xxx_fops =

{

    .owner     =    THIS_MODULE,

    .open      =    xxx_open,

    .release   =    xxx_release,

    .ioctl     =    xxx_ioctl,

    .read      =    xxx_read,

    .write     =    xxx_write,

};

struct cdev xxx_cdev;

struct class *xxx_class;

Intxxx_init(void)

{

int res, err;

    dev_t dev = MKDEV(xxx_major, 0);

    if (xxx_major)

    {

        res = register_chrdev_region(dev, 1,DEVICE_NAME); //固定分配

    }

    else

    {

        res = alloc_chrdev_region(&dev, 0,1, DEVICE_NAME);//动态分配

        xxx_major = MAJOR(dev);

    }

 

    if (res < 0)

    {

        printk("xxx_device: unable to getmajor %d/n", xxx_major);

        return res;

 

    }

 

    if (xxx_major == 0)

    {

        xxx_major = res;

    }

    cdev_init(&xxx_cdev, &xxx_fops);

       xxx_cdev.owner = THIS_MODULE;

       xxx_cdev.ops = &xxx_fops;

       err = cdev_add (&xxx_cdev, dev, 1);//注册cdev

       if (err)

       {

              printk ("Error %d adding xxx%d",err, 0);

       }

    xxx_class = class_create(THIS_MODULE,"xxx_class");

    device_create(xxx_class, NULL, dev,DEVICE_NAME, "xxx%d", 0);

    other_init();

}

 

int xxx_exit(void)

{

       xxx_release(NULL, NULL);

cdev_del(&xxx_cdev);

device_destroy(xxx_class, MKDEV(xxx_major, 0));

class_destroy(xxx_class);

unregister_chrdev_region(MKDEV(xxx_major, 0), 1);

}

对于xxx_open, 可以简单提示一些信息,表示此驱动加载。同样xxx_release也可以用简单的提示一些信息,表明关闭驱动了。

重点是在xxx_ioctl函数,我们基本所有的操作就在这里了。

static intepi_ioctl(struct inode *inode, struct file *file,

                                   unsigned intcmd, unsigned long arg)

{

   char val = 1;

   switch (cmd)

   {

     case IO_CMD_TEST_SET:

     copy_from_user(&val, (char *)arg, 1);

        printk(“%d”, val);

     break;

case IO_CMD_TEST_GET:

     copy_to_user( (char *)arg, &val, 1);

     break;

     default:

       break;

   }

   return 0;

}

       以上所实现的是从arg中取得数据,把数据放到arg中两个功能。用到了copy_from_usrcopy_to_usr两个函数。为什么有这两个函数:是因为驱动是工作内核空间,应用程序是工作在用户空间。如果直接用memcpy这些函数,可能内核空间的数据已经实效,那么复制到用户空间的则可能得到错误的信息。

 

       写完驱动,就可进行编译,那么要写Makefile以方便编译。

obj-m += xxx.o

#内核代码树位置

KDIR:=/home/alsa/kernel/linux_2.6.28

#设置编译器

CROSS_COMPILE = arm-none-linux-gnueabi-

CC := $(CROSS_COMPILE)gcc

LD := $(CROSS_COMPILE)ld

PWD=$(shell pwd)

all: build install

build: clean

       $(MAKE)-C $(KDIR) M=$(PWD) modules

clean:

       rm-rf *.o *.ko

保存,后运行make,则在当前目录下生成xxx.ko文件。

 

最后通过加载驱动:

加载: insmod xxx.ko

卸载:  rmmod xxx

原创粉丝点击