输入子系统(input)框架解析(基于linux3.4.2)

author author     2022-12-04     567

关键词:


我们自己写驱动的流程一般是:

  1. 自己确定或由系统自动分配主设备号;
  2. 建立fops结构;
  3. 使用register_chrdev在初始化函数中进行注册;
  4. 定义入口函数MODULE_INIT()和出口函数MODULE_EXIT()。

但这种我们自己写的驱动程序,只有自己可以调用。因为这种驱动不标准,只有别人知道驱动用法的情况下才能使用。当我们使用QT等标准程序时,这类标准程序不能打开像我们这样的野驱动,所以,我们应该让我们的驱动程序融入“标准”中去。这个标准就是linux提供的输入子系统框架。

1、Linux输入子系统框架

下图是input输入子系统框架:




输入子系统(input)框架解析(基于Linux3.4.2)_python


image-20210714215436333


输入子系统由设备驱动层(Device)、核心层(InputCore)、事件处理层(EventHandler)三部份组成。一个输入事件(如鼠标移动,键盘按键按下,joystick的移动等等)通过input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

  1. 设备驱动程序层:主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
  2. 核心层:为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
  3. 事件处理层:用户编程的接口(设备节点),并处理驱动层提交的数据处理。


输入子系统(input)框架解析(基于Linux3.4.2)_linux_02


image-20210712223400336


  • /dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。
  • 事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。
  • 输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

2、核心层:driver/input/input.c

分析一个驱动程序,首先看他的入口函数,即init初始化函数:

static const struct file_operations input_fops = 
.owner = THIS_MODULE,
.open = input_open_file,
;

static int __init input_init(void)

int err;

err = class_register(&input_class);
if (err)
printk(KERN_ERR "input: unable to register input_dev class\\n");
return err;


err = input_proc_init();
if (err)
goto fail1;

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err)
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
goto fail2;


return 0;

fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
  • 在初始化函数中我们可以看出这只是执行了一个普通的字符设备注册过程,创建了一个input类,在该类下并没有创建具体设备,其余没有什么特别。在字符设备注册时的操作函数集合input_fops中只有一个open函数,直观上看一个open函数并不能执行read等操作。那我们分析一下这个open函数究竟做了些什么:
static int input_open_file(struct inode *inode, struct file *file)

struct input_handler *handler = input_table[iminor(inode) >> 5];
const struct file_operations *old_fops, *new_fops = NULL;
int err;

/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV;

/*
* Thats _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open)
fops_put(new_fops);
return -ENODEV;

old_fops = file->f_op;
file->f_op = new_fops;

err = new_fops->open(inode, file);

if (err)
fops_put(file->f_op);
file->f_op = fops_get(old_fops);

fops_put(old_fops);
return err;
  1. 其中​​iminor(inode)​​​函数调用了​​MINOR(inode->i_rdev)​​​读取子设备号,然后将子设备除以32,找到新挂载的input驱动的数组号,然后放在​​input_handler​​​驱动处理函数​​handler​​中。因为输入子系统支持的设备大类就那么几项,每项支持最多32个设备,除32意味着可将在这32区段的设备都能准确定位到自己对应的大类上,这些设备都可以使用对应大类的公共fops。
  1. 例如:输入子系统的事件设备evdev,次设备号起始位置为64,之后32个设备都属于evdev设备,​​input_table[iminor(inode) >> 5]​​,次设备号64~95的设备都对应input_table[2],这个数组位置指向的是evdev的handler,这个区段内设备的fops,在这个函数中都会指向evdev设备共用的fops,这样就不用驱动编写者自己编写fops,直接使用该设备类型下别人写出的fops即可。
  1. 若handler有值,说明挂载有这个驱动。就将handler结构体里的成员file_operations *fops赋到新的file_operations *new_fops里面。
  1. 再将新的file_operations *new_fops赋到file-> file_operations *f_op里, 此时input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,即实现一个偷天换日的效果。
  1. 然后调用新挂载的input驱动的old_fops里面的成员.open函数,打开驱动open函数
  • 为什么除以32:linux输入子系统,作为将输入设备标准化处理的一种方式,使别人使用这类驱动时不必关心驱动细节即可使用。输入设备分为好多类型,键盘类、鼠标类、触摸屏类等等,linux将输入子系统设备的主设备号定为13,次设备号以32为间隔细分了几大类,(例如事件设备evdev,属于次设备号为64起始向后32个成员都属于事件设备分段,64-95这些设备都属于事件设备),这些设备次设备号除以32结果都是2,在调用驱动操作时他们都可以使用事件设备提供的fops,从而不用驱动编写者自己编写fops,大大提高了驱动易用性
  • 分析:struct input_handler *handler = input_table[iminor(inode) >> 5];
  • 将传入的节点的次设备号除以32(右移5位),并以其作为索引,将数组input_table[]中的某一项赋值给handle。
  • 创建文件操作结构体new_fops,并将其用刚刚的handle结构体中的fops初始化,实现复制。
  • 那么存放各类输入设备的input_handler结构体数组input_table是由谁构造,怎么初始化的呢?
    全局搜索发现其被​​input_register_handler​​调用:
int input_register_handler(struct input_handler *handler)

struct input_dev *dev;

INIT_LIST_HEAD(&handler->h_list);
//判断传进来的文件操作结合不是空的进行进一步操作
if (handler->fops != NULL)

//如果分配的位置已经有值不为空,说明此位置已经被占用,返回EBUSY
if (input_table[handler->minor >> 5])
return -EBUSY;
// 将 handler 放入 input_table数组,数组序号为次设备号/32
input_table[handler->minor >> 5] = handler;

// 将 handler 放入 input_handler_list 链表
list_add_tail(&handler->node, &input_handler_list);

// 取出 input_dev_list 链表中的每一个dev与该handler进行比对
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();
return 0;
  • 全局搜索​​input_register_handler​​​,发现​​evdev.c joydev.c mousedev.c​​​等等都是通过​​input_register_handler​​向核心层注册自己的结构:
----input_register_handler Matches (10 in 9 files) ----
evbug_init in evbug.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) : return input_register_handler(&evbug_handler);
evdev_init in evdev.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) : return input_register_handler(&evdev_handler);
input.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) line 1182 : int input_register_handler(struct input_handler *handler)
input.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) line 1203 : EXPORT_SYMBOL(input_register_handler);
input.h (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\include\\linux) line 1130 : int input_register_handler(struct input_handler *);
joydev_init in joydev.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) : return input_register_handler(&joydev_handler);
kbd_init in keyboard.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\char) : error = input_register_handler(&kbd_handler);
mousedev_init in mousedev.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) : error = input_register_handler(&mousedev_handler);
rfkill_handler_init in rfkill-input.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\net\\rfkill) : return input_register_handler(&rfkill_handler);
tsdev_init in tsdev.c (F:\\SourceInsightProj\\JZ2440_2.6\\linux-2.6.22.6\\drivers\\input) : return input_register_handler(&tsdev_handler);
  • 分析其中一个​​evdev.c​​,其入口函数如下:
static int __init evdev_init(void)

return input_register_handler(&evdev_handler);
  • 看看​​evdev_handler​​结构体的定义:
static struct input_handler evdev_handler = 
.event = evdev_event,
//.connect:连接函数,将设备input_dev和某个input_handler建立连接
.connect = evdev_connect,

.disconnect = evdev_disconnect,

//.fops:文件操作结构体,其中evdev_fops函数就是自己的写的操作函数,然后赋到.fops中
.fops = &evdev_fops,

//.minor:用来存放次设备号
/*其中EVDEV_MINOR_BASE=64, 然后调用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中,所以当open打开这个input设备,就会进入 input_open_file()函数,执行evdev_handler-> evdev_fops -> .open函数*/
.minor = EVDEV_MINOR_BASE,

.name = "evdev",

/*.id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev->的id和某个input_handler的id_table相匹配,就会调用.connect连接函数*/
.id_table = evdev_ids,
;

static const struct file_operations evdev_fops =
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush,
.llseek = no_llseek,
;
  • input_register_device()函数,如何创建驱动设备
int input_register_device(struct input_dev *dev)   //*dev:要注册的驱动设备

... ...
list_add_tail(&dev->node, &input_dev_list); //(1)放入链表中
... ...
list_for_each_entry(handler, &input_handler_list, node) //(2)
input_attach_handler(dev, handler);
... ...

步骤如下:

(1)将要注册的input_dev驱动设备链入input_dev_list链表中,其中input_handler_list在前面讲过,就是存放每个input_handle驱动处理结构体。
(2)然后list_for_each_entry()函数会将每个input_handle从链表中取出并放到handler中,然后再调用input_attach_handler()函数,判断每个input_handle的id_table能否支持当前dev设备,若两者支持便进行连接。

  • 回过头来看注册input_handler​input_register_handler()​函数:
int input_register_handler(struct input_handler *handler)

struct input_dev *dev;
int retval;

retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;

INIT_LIST_HEAD(&handler->h_list);

if (handler->fops != NULL)
if (input_table[handler->minor >> 5])
retval = -EBUSY;
goto out;

input_table[handler->minor >> 5] = handler;


list_add_tail(&handler->node, &input_handler_list);

list_for_each_entry(dev, &input_dev_list, node)//在设备列表中找出设备结构体
input_attach_handler(dev, handler);//设备结构体中的id与handler中的id进行匹配

input_wakeup_procfs_readers();

out:
mutex_unlock(&input_mutex);
return retval;

发现:不管新添加input_dev还是input_handler,都会调用input_attach_handler()判断两者id是否匹配, 若两者匹配便进行连接

  • input_attach_handler()如何实现匹配两者id
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

... ...
id = input_match_device(handler->id_table, dev); //匹配两者

if (!id) //若不匹配,return退出
return -ENODEV;

error = handler->connect(handler, dev, id); //调用input_handler->connect函数建立连接
... ...

发现:若两者匹配成功,就会自动进入input_handler 的connect函数建立连接

  • evdev.c(事件驱动) 的​evdev_handler->connect​函数来分析是怎样建立连接的
  • ​evdev_handler​​​的​​.connect​​​函数是​​evdev_connect()​​,代码如下:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) 

... ...
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); //查找驱动设备的子设备号
if (minor == EVDEV_MINORS) // EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
printk(KERN_ERR "evdev: no more free evdev devices\\n");
return -ENFILE; //没找到驱动设备

... ...
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一个input_handle全局结构体(没有r)
... ...
evdev->handle.dev = dev; //指向参数input_dev驱动设备
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; //指向参数 input_handler驱动处理结构体
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor); //(1)保存驱动设备名字, event%d
... ...
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), //(2) 将主设备号和次设备号转换成dev_t类型
cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
// (3)在input类下创建驱动设备
... ...
error = input_register_handle(&evdev->handle); //(4)注册这个input_handle结构体
... ...
  • 是在保存驱动设备名字,名为​​event%d​​​, 因为没有设置子设备号,默认从小到大排列,其中​​event0​​​是表示这个​​input​​​子系统,所以这个键盘驱动名字就是​​event1​
  • 是在保存驱动设备的主次设备号,其中主设备号​​INPUT_MAJOR=13​​​,因为​​EVDEV_MINOR_BASE=64​​​,所以此设备号=​​64​​+驱动程序本事子设备号
  • 在之前在​​2​​​小结里就分析了​​input_class​​​类结构,会在​​/sys/class/input​​​类下创建驱动设备​​event%d​
  • 最终会进入​​input_register_handle()​​函数来注册,代码在下面
int input_register_handle(struct input_handle *handle)

struct input_handler *handler = handle->handler; //handler= input_handler驱动处理结构体

list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
list_add_tail(&handle->h_node, &handler->h_list); // (2)

if (handler->start)
handler->start(handle);
return 0;
  • 因为​​handle->dev​​指向​​input_dev​​驱动设备,所以就是将​​handle->d_node​​放入到​​input_dev​​驱动设备的​​h_list​​链表中,即​​input_dev​​驱动设备的​​h_list​​链表就指向​​handle->d_node​​。
  • 同样,​​input_handler​​驱动处理结构体的​​h_list​​也指向了​​handle->h_node​​,两者的​​.h_list​​都指向了同一个​​handle​​结构体,然后通过​​.h_list​​ 来找到​​handle​​的成员​​.dev​​和​​handler​​,便能找到对方建立连接了。
  • 建立了连接后,如何读取evdev.c(事件驱动) 的​evdev_handler->.fops->.read​函数呢?

事件驱动的​​.read​​​函数是​​evdev_read()​​函数,我们来分析下:

static ssize_t evdev_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos)

... ...
/*判断应用层要读取的数据是否正确*/
if (count < evdev_event_size())
return -EINVAL;

/*在无数据且是非阻塞操作情况下打开,则立刻返回*/
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;

/*否则,进入睡眠状态 */
retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
... ... //上传数据
  • 若read函数进入了休眠状态,又是谁来唤醒

通过搜索​​evdev->wait​​​这个等待队列变量,找到​​evdev_event​​被谁唤醒:

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)

... ...
wake_up_interruptible(&evdev->wait); //有事件触发,便唤醒等待中断

其中​​evdev_event()​​​是​​evdev.c​​​(事件驱动)的​​evdev_handler->.event​​​成员。当有事件发生了(比如对于按键驱动,当有按键按下时),就会进入​​.event​​函数中去处理事件。

  • 是谁调用evdev_event()这个​.event​事件驱动函数?

应该就是之前分析的​​input_dev​​​那层调用的,我们来看看内核 ​​gpio_keys_isr()​​​函数代码例子就知道了​​(driver/input/keyboard/gpio_key.c)​

static irqreturn_t gpio_keys_isr(int irq, void *dev_id)

/*获取按键值,赋到state里*/
... ...
/*上报事件*/
input_event(input, type, button->code, !!state);
input_sync(input); //同步信号通知,表示事件发送完毕

显然就是通过​​input_event()​​​来调用​​.event​​事件函数,我们来看看:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

struct input_handle *handle;
... ...

/* 通过input_dev->h_list链表找到input_handle驱动处理结构体*/
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open) //如果input_handle之前open 过,那么这个就是我们的驱动处理结构体
handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数

若之前驱动​​input_dev​​​和处理​​input_handler​​​已经通过​​input_handler​​​的​​.connect​​​函数建立起了连接,那么就调用​​evdev_event()​​​的​​.event​​事件函数。

总结:

  • 1.注册输入子系统,进入​​input_init()​​:
  • 1)创建主设备号为​​13​​​的input字符设备:​​register_chrdev(INPUT_MAJOR, "input", &input_fops);​
  • 2.open打开驱动,进入input_open_file():
  • 1)更新设备的file_oprations:​​file->f_op=fops_get(handler->fops);​
  • 2)执行file_oprations->open函数:​​new_fops->open(inode, file);​
  • 3.注册input_handler,进入input_register_handler():
  • 1)添加到input_table[]处理数组中:​​input_table[handler->minor >> 5] = handler;​
  • 2)添加到input_handler_list链表中:​​list_add_tail(&handler->node, &input_handler_list);​
  • 3)判断input_dev的id,看是否有支持这个驱动的设备:
  • 遍历查找input_dev_list链表里所有input_dev:​​list_for_each_entry(dev, &input_dev_list, node)​​。
  • 判断两者id,若两者支持便进行连接:​​input_attach_handler(dev, handler);​
  • 4.注册input_dev,进入input_register_device():
  • 1)放在input_dev_list链表中:​​list_add_tail(&dev->node, &input_dev_list);​
  • 2)判断input_handler的id,是否有支持这个设备的驱动:
  • 遍历查找input_handler_list链表里所有input_handler:​​list_for_each_entry(handler, &input_handler_list, node)​
  • 判断两者id,若两者支持便进行连接:​​input_attach_handler(dev, handler);​
  • 5.判断input_handler和input_dev的id,进入input_attach_handler():
  • 1)匹配两者id:​​input_match_device(handler->id_table, dev);​
  • 2)匹配成功,则建立连接。调用:​​input_handler ->connec(handler, dev, id);​
  • 6.建立input_handler和input_dev的连接,进入input_handler->connect():
  • 1)创建全局结构体,通过input_handle结构体连接双方
  • 创建两者连接的input_handle全局结构体:​​evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);​
  • 连接input_dev->h_list:​​list_add_tail(&handle->d_node, &handle->dev->h_list);​
  • 连接input_handle->h_list:​​list_add_tail(&handle->h_node, &handler->h_list);​
  • 7.有事件发生时(比如按键中断),在中断函数中需要进入input_event()上报事件:
  • 1)找到驱动处理结构体,然后执行input_handler->event():
  • 通过input_dev ->h_list链表找到input_handle驱动处理结构体:​​list_for_each_entry(handle, &dev->h_list, d_node)​
  • 如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式):​​if (handle->open)​
  • 调用evdev_event()的.event事件函数:​​handle->handler->event(handle, type, code, value);​

输入子系统框架分析

 drivers/input/input.c: input_init>err=register_chrdev(INPUT_MAJOR,"input",&input_fops); staticconststructfile_operationsinput_fops={ .owner=THIS_MODULE, .open=input_open_file,};问:怎么读按键?in 查看详情

基于输入子系统的按键驱动程序

输入子系统框图: 基于输入子系统的按键驱动程序步骤:1.分配input_dev结构体 2.设置这个结构体 3.注册 4.硬件相关操作(有数据产生时调用input_event来上报)。 1、分配input_dev结构体首先要定义这个结构体:stat... 查看详情

移值linux3.4.2内核之框架及初步修改(代码片段)

前言先类比下WindowsPC的启动流程,一上电后BIOS会去引导扇区读取系统引导程序引导windows内核的启动,内核启动过程中会去识别C盘,D盘,装载驱动程序,启动应用,对于嵌入式LINUX来说,BIOS称为Bootloader,它主要完成的工作有如下3步1.装载... 查看详情

输入子系统框架(代码片段)

...动框架架构测试hexdump分析tty读取分析按键连发title:输入子系统框架tags:linuxdate:201 查看详情

移值linux3.4.2内核之框架及初步修改(代码片段)

前言先类比下WindowsPC的启动流程,一上电后BIOS会去引导扇区读取系统引导程序引导windows内核的启动,内核启动过程中会去识别C盘,D盘,装载驱动程序,启动应用,对于嵌入式LINUX来说,BIOS称为Bootloader,它主要完成的工作有如下3步1.装载... 查看详情

python登录系统简易框架

#!/usr/bin/envpython #coding:utf8whileTrue:  user=raw_input("请输入用户名:")  ifuser==(‘isaac‘):    password=raw_input(‘请输入您的密码:‘)    whileTr 查看详情

嵌入 OSGi 框架:啥时候应该完成捆绑解析?

】嵌入OSGi框架:啥时候应该完成捆绑解析?【英文标题】:EmbeddingOSGiframework:whendoesbundleresolutionshouldbedone?嵌入OSGi框架:什么时候应该完成捆绑解析?【发布时间】:2013-11-1020:14:27【问题描述】:目标我目前正在调整现有的Java应... 查看详情

spi在linux3.14.78fs_s5pc100(cortexa8)和s3c2440上驱动移植(deepdive)

   由于工作的原因,对SPI的理解最为深刻,也和SPI最有感情了,之前工作都是基于OSEK操作系统上进行实现,也在US/OS3上实现过SPI驱动的实现和测试,但是都是基于基本的寄存器操作,没有一个系统软件架构的思想,感觉li... 查看详情

beego框架之请求数据处理(代码片段)

...以通过如下方式获取数据:通过this.Getstring("获取用户输入")获取用户输入再通过this.Ctx.WriteString("输出用户输入的内容")输出用户输入的内容通过Input.Get("")获取用户输入直接解析到str 查看详情

mui框架之输入框input

input输入框的官方api文档:http://dev.dcloud.net.cn/mui/ui/#input一、输入框类型输入框的类型是根据type来决定是普通输入框还是密码框,搜索框等类型<label>帐号</label><input id="mess" class="mui-input-clear" type="text"  查看详情

input输入子系统分析

//input输入子系统分析//刘术河2016.08.22linux-2.6.39-at91-2016.08.11-lshdriversinputInput.c该文件下有input_register_device和input_register_handler函数接口0.先分析input.c的核心层架构 input_init(void) class_register(&input_c 查看详情

coreaudio框架详细解析(二)——基于coreaudio的ios音频服务总结分析

...音频处理的基础,它是应用程序用来处理音频的一组软件框架,所有关于iOS音频开发的接口都是由CoreAudio来提供或者经过它提供的接口来进行封装的。其实一句话,它是任何iOS或者MAC系统音频处理框架的基础。具体可以用官方文... 查看详情

输入子系统------键盘按键驱动程序(代码片段)

由上一节的输入子系统的框架分析可知,其分三层:设备驱动层,核心层,事件驱动层我们在为某种设备的编写驱动层,只需要关心设备驱动层,即如何驱动设备并获得硬件数据(如按下的按键数据),然后调用核心层提供的接... 查看详情

struts2的输入校验

Struts2提供了基于验证框架的输入校验,在这种校验方式下,所有的输入校验只需要编写简单的配置文件,Struts2的验证框架将会负责进行服务器校验和客户端校验。校验失败后将Struts2将自动返回名为“input”的Result,如需... 查看详情

mapreduce框架原理inputformat数据输入(代码片段)

MapReduce框架原理-InputFormat数据输入1.切片与MapTask并行度决定机制1)问题引出2)MapTask并行度决定机制2.Job提交流程源码和切片源码详解1)Job提交流程源码详解2)FileInputFormat切片源码解析(input.getSplits(job))... 查看详情

input子系统(代码片段)

...的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件处理层)、 查看详情

为linux3.4.2内核编写led驱动

...交叉编译器:arm-linux-gcc(version4.3.2)1、linux字符设备驱动框架用户应用程序通过调用C库里已经实现的open、read、write等库函数来操作文件(在Linux中,一切皆文件,所有硬件设备在内核看来均是文件)。库函数(open等)的调用引... 查看详情

安徽微享商盟系统解析和开发

...着技术的发展,我们已经看到许多基于Web技术的移动开发框架,微享商盟系统开发158*/*899**4/2250*/小温*/现在你只需要牢固掌握HTML、CSS和Javascript技术就可以开发出一个移动App来。对于大部分Web开发人员,HTML、CSS和Javascript是他们... 查看详情