Adroid Architecture Analysis and driver keyboard
I. user space
Adroid 2.1 architecture resolution 7 keyboard
The above link details the operation of the android user space buttons. Finally, by constantly polling all devices until the device status generated by the Pollin event is read: If (MFDs [I]. revents & Pollin) {res
= Read (MFDs [I]. FD, & IEV, sizeof (IEV ));......
Ii. Underlying driver
1. device registration
Static struct Resource
Initi_keypad_resource [] = {
[0] = {
. Start = maid,
. End = 89c64xx_pa_keypad + S3C64XX_SZ_KEYPAD-1,
. Flags = ioresource_mem,
},
[1] = {
. Start = irq_keypad,
. End = irq_keypad,
. Flags = ioresource_irq,
}
};
Struct platform_device
Initi_device_keypad = {
. Name = "s3c-keypad ",
. ID =-1,
. Num_resource = array_size (initi_keypad_resource ),
. Resource = maid,
};
Export_symbol (initi_device_keypad );
Static struct platform_device
* Smdk6410_devices [] _ initdata = {
...
& Amp; cloud_device_keypad,
}
Static void _ init smdk6410_machine_init (void)
{
...
Platform_add_devices (smdk6410_devices, array_size (smdk6410_devices ));
...
}
Int platform_add_devices (struct platform_device ** devs, int num)
{
Int I, ret = 0;
For (I = 0; I <num; I ++ ){
Ret = platform_device_register (Devs [I]);
If (RET ){
While (-- I> = 0)
Platform_device_unregister (Devs [I]);
Break;
}
}
Return ret;
}
2. Driver registration and cancellation
Module_init (initi_keypad_init );
Module_exit (initi_keypad_exit );
Static int _ init initi_keypad_init (void)
{
Int ret;
Ret = platform_driver_register (& initi_keypad_driver );
If (! RET)
Printk (kern_info "s3c keypad driver \ n ");
Return ret;
}
Static void _ exit initi_keypad_exit (void)
{
Platform_driver_unregister (& initi_keypad_driver );
}
Platform_driver struct:
Static struct platform_driver
Initi_keypad_driver = {
. Probe = maid,
. Remove = maid,
. Suspend = maid,
. Resume = maid,
. Driver = {
. Owner = this_module,
. Name = "s3c-keypad ",
},
};
3. Device Detection
Static int _ init initi_keypad_probe (struct platform_device * pdev)
{
Struct resource * res, * keypad_mem, * keypad_irq = NULL;
Struct input_dev * input_dev;
Int ret, size;
Int key, code;
Struct initi_keypad_extra * extra = NULL;
Struct initi_keypad_gpio_key * gpio_key;
Int I;
Char * input_dev_name;
Res = platform_get_resource (pdev, ioresource_mem, 0 );
// Obtain the device memory information
If (RES = NULL ){
Dev_err (& pdev-> Dev, "no memory resource specified \ n ");
Return-enoent;
}
Size = (res-> end-res-> Start) + 1;
Keypad_mem = request_mem_region (res-> Start, size, pdev-> name );
If (keypad_mem = NULL ){
Dev_err (& pdev-> Dev, "failed to get memory region \ n ");
Ret =-enoent;
Goto err_req;
}
Key_base = ioremap (res-> Start, size); // Io space ing
If (key_base = NULL ){
Printk (kern_err "failed to remap register block \ n ");
Ret =-enomem;
Goto err_map;
}
Keypad_clock = clk_get (& pdev-> Dev, "keypad ");
// Obtain the clock corresponding to the keypad
If (is_err (keypad_clock )){
Dev_err (& pdev-> Dev, "failed to find keypad clock source \ n ");
Ret = ptr_err (keypad_clock );
Goto err_clk;
}
Clk_enable (keypad_clock); // enable the clock of the keypad Module
Initi_keypad_data = kzarloc (sizeof (struct initi_keypad), gfp_kernel );
Input_dev = input_allocate_device (); // apply for an input_dev Device
Input_dev_name = (char *) kmalloc (sizeof ("s3c-keypad-revxxxx"), gfp_kernel );
// Here is the key value configuration table mentioned in the user space.
If (! Initi_keypad_data |! Input_dev ){
Ret =-enomem;
Goto err_alloc;
}
Platform_set_drvdata (pdev, initi_keypad_data );
For (I = 0; I <sizeof (initi_keypad_extra)/sizeof (struct initi_keypad_extra); I ++)
{
If (maid [I]. board_num = system_rev ){
Extra = & initi_keypad_extra [I];
Sprintf (input_dev_name, "% S % 04x", device_name, "-rev", system_rev );
Dprintk (": Board rev 0x % 04x is detected! \ N ", initi_keypad_extra [I]. board_num );
Break;
}
}
If (! Extra ){
Extra = & initi_keypad_extra [0];
Sprintf (input_dev_name, "% S % s", device_name, "-rev0000"); // default revison
Dprintk (": failed to detect board rev. Set Default rev00 \ n ");
}
Dprintk (": Input Device Name: % S. \ n", input_dev_name );
Initi_keypad_data-> Dev = input_dev;
Initi_keypad_data-> extra = extra;
Gpio_key = extra-> gpio_key;
/* Create and register the input driver */
Set_bit (ev_key, input_dev-> evbit );
Set_bit (ev_rep, input_dev-> evbit );
Initi_keypad_data-> nr_rows = keypad_rows;
Initi_keypad_data-> no_cols = keypad_columns;
For (Key = 0; key <32; key ++ ){
Code = maid [Key] = keypad_keycode [Key];
If (Code <= 0)
Continue;
Set_bit (Code & key_max, input_dev-> keybit );
}
For (I = 0; I <Extra-> gpio_key_num; I ++ ){
Input_set_capability (input_dev, ev_key, (gpio_key + I)-> keycode );
}
Input_dev-> name = device_name;
Input_dev-> phys = "s3c-keypad/input0 ";
Input_dev-> ID. bustype = bus_host;
Input_dev-> ID. Vendor = 0x0001;
Input_dev-> ID. Product = 0x0001;
Input_dev-> ID. Version = 0x0001;
Input_dev-> keycode = keypad_keycode;
/* Init_timer (& keypad_timer );*/
Keypad_timer.function = keypad_timer_handler;
Keypad_timer.data = (unsigned long) maid;
For (I = 0; I <Extra-> gpio_key_num; I ++, gpio_key + = 1)
{
Initi_gpio_cfgpin (gpio_key-> gpio, initi_gpio_sfn (gpio_key-> gpio_af ));
Initi_gpio_setpull (gpio_key-> gpio, initi_gpio_pull_none );
Set_irq_type (gpio_key-> Eint, irq_type_edge_both );
Ret = request_irq (gpio_key-> Eint, gpio_int_handler, ir1__disabled,
"Initi_keypad gpio key", (void *) initi_keypad_data );
// Interrupt Registration
If (RET ){
Printk (kern_err "request_irq (% d) failed (IRQ for gpio key )!!! \ N ", gpio_key-> Eint );
Ret =-EIO;
Goto err_irq;
}
}
Ret = input_register_device (input_dev); // register this device with the input subsystem
If (RET ){
Printk ("unable to register s3c-keypad input device !!! \ N ");
Goto out;
}
Return 0;
Out:
Input_free_device (input_dev );
Kfree (initi_keypad_data );
Err_irq:
Free_irq (keypad_irq-> Start, input_dev );
Free_irq (keypad_irq-> end, input_dev );
Gpio_key = extra-> gpio_key;
For (I = 0; I <Extra-> gpio_key_num; I ++, gpio_key + = 1)
Free_irq (gpio_key-> Eint, initi_keypad_data );
Err_alloc:
Clk_disable (keypad_clock );
Clk_put (keypad_clock );
Err_clk:
Iounmap (key_base );
Err_map:
Release_resource (keypad_mem );
Kfree (keypad_mem );
Err_req:
Return ret;
}
4. Key departure interruption, interrupt processing and data upload
Static irqreturn_t gpio_int_handler (int irq, void * dev_id)
{
Struct input_dev * Dev = initi_keypad_data-> dev;
Struct initi_keypad_extra * extra = initi_keypad_data-> extra;
Struct initi_keypad_gpio_key * gpio_key = extra-> gpio_key;
Int I, State;
For (I = 0; I <Extra-> gpio_key_num; I ++)
{
If (gpio_key [I]. Eint = IRQ)
{
Gpio_key = & gpio_key [I];
Break;
}
}
If (gpio_key! = NULL)
{
State = gpio_get_value (gpio_key-> gpio );
State ^ = gpio_key-> state_upset;
If (state ){
Input_report_key (Dev, gpio_key-> keycode, 1 );
}
Else {
Input_report_key (Dev, gpio_key-> keycode, 0 );
}
}
Return irq_handled;
}
Data upload input_report_key-input_event-input_handle_event-input_pass_event-handle-> Handler-> event (corresponding function: evdev_event)-> data temporary event (input_event struct ), for user space read/write operations.
5. Functions of input events during transmission
Void input_event (struct input_dev * Dev, unsigned int type, unsigned int code, int value)
{
Unsigned long flags;
If (is_event_supported (type, Dev-> evbit, ev_max )){
Spin_lock_irqsave (& Dev-> event_lock, flags );
Add_input_randomness (type, code, value); // keys are a good source of random numbers.
Input_handle_event (Dev, type, code, value );
Spin_unlock_irqrestore (& Dev-> event_lock, flags );
}
}
Static void input_handle_event (struct input_dev * Dev, unsigned int type, unsigned int code, int value)
{
...
Case ev_key:
If (is_event_supported (Code, Dev-> keybit, key_max )&&
!! Test_bit (Code, Dev-> key )! = Value) {// whether this time is a new key value
If (value! = 2 ){
_ Change_bit (Code, Dev-> key); // The bitmap corresponding to the code is reversed through an exception or ^ operation. If the value is equal to 2, this button is ignored.
If (value)
Input_start_autorepeat (Dev, Code); // press the keyboard to enable timed detection. This allows continuous input.
}
Disposition = input_pass_to_handlers;
}
Break;
...
}
Static void input_start_autorepeat (struct input_dev * Dev, int code)
{
If (test_bit (ev_rep, Dev-> evbit )&&
Dev-> rep [rep_period] & Dev-> rep [rep_delay] &
Dev-> timer. Data ){
Dev-> repeat_key = code;
Mod_timer (& Dev-> timer, // restart the timer input_repeat_key, interval msecs_to_jiffies (Dev-> rep [rep_delay])
Jiffies + msecs_to_jiffies (Dev-> rep [rep_delay]);
}
}
Static void input_repeat_key (unsigned long data)
{
Struct input_dev * Dev = (void *) data;
Unsigned long flags;
Spin_lock_irqsave (& Dev-> event_lock, flags );
If (test_bit (Dev-> repeat_key, Dev-> key )&&
Is_event_supported (Dev-> repeat_key, Dev-> keybit, key_max )){
Input_pass_event (Dev, ev_key, Dev-> repeat_key, 2); // hand it over to the processing Button Function
If (Dev-> sync ){
/*
* Only send syn_report if we are not in a middle
* Of driver parsing a new hardware packet.
* Otherwise assume that the driver will send
* Syn_report once it's done.
*/
Input_pass_event (Dev, ev_syn, syn_report, 1 );
}
If (Dev-> rep [rep_period])
Mod_timer (& Dev-> timer, jiffies +
Msecs_to_jiffies (Dev-> rep [rep_period]);
}
Spin_unlock_irqrestore (& Dev-> event_lock, flags );
}
The above driver is completed in input mode. For details, refer to the Linux input sub-device.