Some time ago, the touch screen and the Optical Mouse responsible for the project were all input devices. After reading the materials and combining the project code, I made a summary, which basically came from my personal understanding and the Internet.
After linux2.6, Linux abstracts the input device and abstracts the input subsystem, which is the middle layer driven by all I/O devices, provides a unified interface for the upper layer, which separates event reporting and processing, and uses a layered mode. In our driver, we only need to pay attention to event reporting, others are handled by Linux itself. In the upper-layer system, it does not need to know how many keyboard, mouse, trackball, touch screen and other devices are on the bottom layer. It only needs to process the reported input events accordingly.
I. Framework of the input subsystem
The input subsystem divides the input driver into three parts: Driver, input core, and event handler. an input event, such as mouse movement, keyboard press, joystick movement, touch screen click and slide, etc., arrives at the user space in the order of driver-> inputcore-> eventhandler-> userspace and passes it to the application.
In our usual driver development, all we need to do is the input driver layer. Others are basically done by Linux, and no changes are required, unless you need to add new feature support (for example, you want to add multi-touch Support)
2. An example of simple use of the input subsystem
In the documentation Documentation/input/input-programming.txt that comes with the kernel. An example of using the input subsystem is provided with relevant instructions. This example can be used by beginners.
# Include <Linux/input. h> <br/> # include <Linux/module. h> <br/> # include <Linux/init. h> <br/> # include <ASM/IRQ. h> <br/> # include <ASM/Io. h> <br/> static struct input_dev * button_dev; <br/> static irqreturn_t button_interrupt (int irq, void * dummy) <br/>{< br/> input_report_key (button_dev, btn_0, INB (button_port) & 1); <br/> input_sync (button_dev); <br/> return irq_handled; <br/>}< br/> static int _ init butto N_init (void) <br/>{< br/> int error; <br/> If (request_irq (button_irq, button_interrupt, 0, "button", null )) {<br/> printk (kern_err "button. c: Can't allocate IRQ % d/N ", button_irq); <br/> return-ebusy; <br/>}< br/> button_dev = input_allocate_device (); <br/> If (! Button_dev) {<br/> printk (kern_err "button. c: not enough memory/N "); <br/> error =-enomem; <br/> goto err_free_irq; <br/>}< br/> button_dev-> evbit [0] = bit_mask (ev_key); <br/> button_dev-> keybit [bit_word (btn_0)] = bit_mask (btn_0); <br/> error = input_register_device (button_dev); <br/> If (error) {<br/> printk (kern_err "button. c: failed to register device/N "); <br/> goto err_free_dev; <br/>}< br/> return 0; <br/> err_free_dev: <br/> input_free_device (button_dev); <br/> err_free_irq: <br/> free_irq (button_irq, button_interrupt); <br/> return error; <br/>}< br/> static void _ exit button_exit (void) <br/>{< br/> input_unregister_device (button_dev); <br/> free_irq (button_irq, button_interrupt); <br/>}< br/> module_init (button_init); <br/> module_exit (button_exit );
This example is relatively simple. There is no specific hardware device, but the program contains the most basic registration process driven by the input subsystem.
1) an input device is applied in the initialization function.
Button_dev = input_allocate_device ();
2) Registration
Input_register_device (button_dev );
3) you also need to apply for an interrupt processing routine to report the received key information to the input subsystem.
Request_irq (button_irq, button_interrupt, 0, "button", null)
4) Another important thing to note is to set the parameters for reporting data to inform the input subsystem of the events it supports reporting.
Button_dev-> evbit [0] = bit_mask (ev_key );
Button_dev-> keybit [bit_word (btn_0)] = bit_mask (btn_0 );
Set the events generated by the device and the reported key values respectively. Struct iput_dev has two members: evbit and keybit, which respectively indicate the actions and key types supported by the device.
5) input_report_key ()
Used to report a button action to the upper layer
Input_sync ()
It is used to tell the upper layer that the reporting event has been completed.
Iii. Input core and event handler
In addition, there are many introductions on the input core and event handler in the input subsystem, which are mainly about the Linux kernel and are rarely used in driver development, this is not the focus of our attention. I will not describe more here. If you are interested, you can go online to find a lot of resources in this area.
Iv. Touch Screen driver in g15 Project
The driver process is basically the same as the above process, but because specific hardware devices are involved, the process will be different in some details and be more complex.
The host uses the IIC Bus to read the required data from the chip, which is usually the absolute coordinates of X and Y, as well as the data flag (single point, multi point, not clicked, that's enough. Some chips also have register values such as gestures, but they are not used in Linux/Android systems. In the upper layer, the changes in coordinates are used to calculate gestures.
The initialization function is as follows:
Static int micco_ts_probe (struct platform_device * pdev) <br/>{< br/> int ret; <br/> struct proc_dir_entry * micco_ts_proc_entry; <br/> micco_td = kzarloc (sizeof (struct micco_ts_data), gfp_kernel); <br/> If (! Micco_td) {<br/> ret =-enomem; <br/> goto micco_ts_out; <br/>}< br/> // micco_td-> pen_state = tsi_pen_up; <br/> pen_state = tsi_pen_up; <br/> micco_td-> suincluded = 0; <br/> micco_td-> use_count = 0; <br/> mutex_init (& mutex ); <br/>/* Register input device */<br/> micco_ts_input_dev = input_allocate_device (); // apply for an input device <br/> If (micco_ts_input_dev = NULL) {<br/> printk (kern_err "% s: failed to allocate input DEV/N" ,__ function _); <br/> return-enomem; <br/>}< br/> // fill in the input struct. These are some attribute descriptions and names of the input device, bus, etc. <br/> micco_ts_input_dev-> name = "Micco-ts"; <br/> micco_ts_input_dev-> phys = "Micco-TS/input0 "; <br/> micco_ts_input_dev-> Dev. parent = & pdev-> dev; <br/> micco_ts_input_dev-> open = new_ts_open; <br/> closed-> close = new_ts_close; <br/> micco_ts_input_dev-> ID. bustype = bus_i2c; <br/> // supported events <br/> set_bit (ev_syn, micco_ts_input_dev-> evbit); <br/> set_bit (ev_key, callback-> evbit); <br/> set_bit (btn_touch, micco_ts_input_dev-> keybit); <br/> set_bit (btn_touch2, micco_ts_input_dev-> keybit ); <br/> set_bit (ev_abs, micco_ts_input_dev-> evbit); <br/> input_set_abs_params (micco_ts_input_dev, abs_mt_position_x, 0, 0x31f, 0, 0 ); <br/> input_set_abs_params (bytes, abs_mt_position_y, 0, 0x1df, 0, 0); <br/> input_set_abs_params (micco_ts_input_dev, bytes, 0,255, 0, 0 ); <br/> input_set_abs_params (micco_ts_input_dev, abs_mt_width_major, 0, 15, 0, 0); <br/> // initialize a work queue, bottom half of the scheduling Interrupt Processing Routine <br/> init_work (& work, new_ts_work ); <br/> // register the input device with the System <br/> ret = input_register_device (micco_ts_input_dev); <br/> If (RET) {<br/> printk (kern_err <br/> "% s: unabled to register input device, ret = % d/N", <br/>__ function __, RET); <br/> return ret; <br/>}< br/> init_ts (); // initialization of the chip <br/> If (gpio_request (mfp_to_gpio (mfp_cfg_pin (tp_int), "TP int ")) {<br/> gpio_free (mfp_to_gpio (mfp_cfg_pin (tp_int); <br/> printk (kern_err "request gpio failed," <br/> "gpio: % d/N ", tp_int); <br/> return 0; <br/>}< br/> gpio_direction_input (mfp_to_gpio (mfp_cfg_pin (tp_int ))); <br/> // use the tp_int foot to apply for an interruption. When the finger is touched to the screen, the interruption will occur and the interrupt processing program will be entered, read and report data <br/> If (request_irq (tp_irq_no, new_ts_isr, kernel, "Micco-ts", null) {<br/> printk (kern_err "micco_touch.c: can't allocate IRQ % d/N ", tp_irq_no); <br/> return-ebusy; <br/>}< br/> // block interruptions, <br/> disable_irq_nosync (tp_irq_no); <br/> // printk ("disable_irq_nosync (tp_irq_no) mark2/N "); <br/> enable_irq_flag = 0; <br/> If (Ret <0) <br/> goto pmic_cb_out; <br/> micco_ts_proc_entry = create_proc_entry ("Driver/micc_ts ", 0, null); <br/> If (micco_ts_proc_entry) {<br/> micco_ts_proc_entry-> write_proc = micco_ts_proc_write; <br/>}< br/> return 0; <br/> pmic_cb_out: <br/> input_unregister_device (micco_ts_input_dev); <br/> micco_ts_out: <br/> kfree (micco_td); <br/> return ret; <br/>}
Top half of interrupt handling
Static irqreturn_t new_ts_isr (int irq, void * dev_data)
{
Schedule_work (& work );
Return irq_handled;
}
Only a simple scheduling of the work queue, the following is the true Interrupt Processing (bottom half)
Static void new_ts_work (struct work_struct * Work) <br/>{< br/> 2010tem_x1, tem_y1; <br/> 2010tem_x2, tem_y2; <br/> u8 pen_down; <br/> int pen_up_already_reported = 0; <br/> mutex_lock (& mutex); <br/> pen_down = tsi2c_byte_read (slave_addr, cap_tp_mode ); <br/> // printk ("new_ts_work pen_down is % x/N", pen_down); <br/> If (129 = pen_down) <br/>{< br/> pen_state = tsi_pen_down; <br/> cap_tp_read (& tem_x1, & tem_y1, 1); <Br/> // dependencies; <br/> input_report_abs (micco_ts_input_dev, latency, 1); <br/> input_report_abs (micco_ts_input_dev, abs_mt_width_major, 1 ); <br/> input_report_abs (values, abs_mt_position_x, tem_x1 & 0x3ff); <br/> input_report_abs (values, values, tem_y1 & 0x3ff); <br/> input_mt_sync (values ); <br/> input_sync (micco_ts_input _ Dev); <br/> pen_up_already_reported = 0; <br/>}< br/> else if (130 = pen_down) <br/>{< br/> pen_state = tsi_pen_down; <br/> cap_tp_read (& tem_x1, & tem_y1, 1); <br/> cap_tp_read (& tem_x2, & tem_y2, 2); <br/> // response; <br/> input_report_abs (micco_ts_input_dev, response, 1); <br/> input_report_abs (micco_ts_input_dev, abs_mt_width_major, 1); <br/> input_report_abs (bytes, abs_mt_position_x, tem_x1 & 0x3ff); <br/> input_report_abs (bytes, abs_mt_position_y, tem_y1 & 0x3ff ); <br/> input_mt_sync (SUCCESS); <br/> input_report_abs (micco_ts_input_dev, success, 100); <br/> input_report_abs (micco_ts_input_dev, success, 1 ); <br/> input_report_abs (micco_ts_input_dev, abs_mt_position_x, TEM _ X2 & 0x3ff); <br/> input_report_abs (bytes, abs_mt_position_y, tem_y2 & 0x3ff); <br/> input_mt_sync (bytes); <br/> input_sync (bytes ); <br/> pen_up_already_reported = 0; <br/>}< br/> else <br/>{< br/> If (pen_state! = Tsi_pen_up )&&! Dependencies) {<br/> input_report_abs (micco_ts_input_dev, latency, 0); <br/> input_report_abs (micco_ts_input_dev, latency, 0); <br/> input_mt_sync (latency ); <br/> input_sync (micco_ts_input_dev); <br/> pen_up_already_reported = 1; <br/>}< br/> pen_state = tsi_pen_up; <br/>}< br/> mutex_unlock (& mutex); <br/>}
5. Single-point touch & multi-point touch
As shown in the above example, the multi-touch function is already included. Now we will briefly introduce the differences between single-point touch and multi-point touch in driver implementation.
To achieve multi-point touch, Kernel support is required. That is to say, the input core and event handler layers in the input subsystem must support multiple points. The multi-point support is added only in Linux 2.6.30, but now many BSP 2.6.29 have installed patches for multi-point touch, which is also usable in g15 project.
First, in the device initialization phase of the probe function
Input_set_abs_params () function. The setting method is different.
Single point:
Input_set_abs_params (micco_ts_input_dev, abs_x, 0, 0x31f, 0, 0 );
Input_set_abs_params (micco_ts_input_dev, abs_y, 0, 0x1df, 0, 0 );
Input_set_abs_params (micco_ts_input_dev, abs_pressure, 0,255, 0, 0 );
Input_set_abs_params (micco_ts_input_dev, abs_tool_width, 0, 15, 0, 0 );
Multiple points:
Input_set_abs_params (micco_ts_input_dev, abs_mt_position_x, 0, 0x31f, 0, 0 );
Input_set_abs_params (micco_ts_input_dev, abs_mt_position_y, 0, 0x1df, 0, 0 );
Input_set_abs_params (micco_ts_input_dev, abs_mt_touch_major, 0,255, 0, 0 );
// Equivalent to the abx_pressure of a single screen
Input_set_abs_params (micco_ts_input_dev, abs_mt_width_major, 0, 15, 0, 0 );
// Equivalent to abs_tool_width of a single screen
Second, the way data is reported.
Single point reporting:
Input_report_abs (micco_ts_input_dev, abs_x, tem_x & 0x3ff );
Input_report_abs (micco_ts_input_dev, abs_y, tem_y & 0x3ff );
Input_report_key (micco_ts_input_dev, btn_touch, 1 );
Input_sync (micco_ts_input_dev );
That is
Abs_x
Abs_y
Syn_report
Multi-Point reporting:
Input_report_abs (micco_ts_input_dev, abs_mt_touch_major, 100 );
Input_report_abs (micco_ts_input_dev, abs_mt_width_major, 1 );
Input_report_abs (micco_ts_input_dev, abs_mt_position_x, tem_x1 & 0x3ff );
Input_report_abs (micco_ts_input_dev, abs_mt_position_y, tem_y1 & 0x3ff );
Input_mt_sync (micco_ts_input_dev );
Input_report_abs (micco_ts_input_dev, abs_mt_touch_major, 100 );
Input_report_abs (micco_ts_input_dev, abs_mt_width_major, 1 );
Input_report_abs (micco_ts_input_dev, abs_mt_position_x, tem_x2 & 0x3ff );
Input_report_abs (micco_ts_input_dev, abs_mt_position_y, tem_y2 & 0x3ff );
Input_mt_sync (micco_ts_input_dev );
Input_sync (micco_ts_input_dev );
That is
For (int I; I <n; I ++)
{
Abs_mt_position_x
Abs_mt_position_y
Syn_mt_report
}
Syn_report
The above are the main differences between single point and multi-point driver development. Of course there are many other differences, such as hardware support, if you are interested in multi-touch, you can carefully read the Linux kernel Documentation/input/multi-touch-protocol.txt