Linux touch screen Driver Analysis

Source: Internet
Author: User

I. Prerequisites

1. Input Subsystem ):

In Linux, the Input subsystem consists of the device driver layer, Input Core, and Event Handler layer of the Input subsystem. The device driver layer provides read/write access to the hardware registers and converts the responses of the underlying hardware to user input access to standard input events, and then submits the requests to the event processing layer through the core layer; the core layer provides the device driver layer programming interface and the event processing layer programming interface; the event processing layer provides unified device access interfaces and event processing submitted by the driver layer for our user space applications. Therefore, the driver of the input device does not focus on the operations on the device file, but on the operations on the hardware registers and the input events submitted. Let's use graphs to describe the relationship between the three!

In addition, I found another figure to illustrate the structure of the Linux input sub-system, which may be easier to understand. As follows:

 

2. Input subsystem device driver layer implementation principle:

In Linux, the Input device is described using the input_dev struct, which is defined in input. h. The device driver can be implemented by following the steps below.
① In the driver module Loading Function, set the events that the Input device supports the input subsystem;
② Register the Input device into the input subsystem;
③ When an Input operation occurs on the Input device (for example, when the keyboard is pressed/lifted, the touch screen is touched/lifted/moved, and the mouse is moved/clicked/lifted ), submits events and corresponding key values/coordinates.

The event types of input devices in Linux are as follows (only common events are listed here. For more information, see linux/input. h ):

EV_SYN 0x00 synchronization event
EV_KEY 0x01 key event
EV_REL 0x02 relative coordinates (for example, when the mouse moves, an offset relative to the last position is reported)
EV_ABS 0x03 absolute coordinates (for example, the touch screen and operation lever report absolute coordinates)
EV_MSC 0x04 others
EV_LED 0x11 LED
EV_SND 0x12 sound
EV_REP 0x14 Repeat
EV_FF 0x15 Force Feedback

The following functions are used to submit commonly used event types to the input subsystem:

Void input_report_key (struct input_dev * dev, unsigned int code, int value); // function used to submit a key event
Void input_report_rel (struct input_dev * dev, unsigned int code, int value); // submit a function for a relative coordinate event
Void input_report_abs (struct input_dev * dev, unsigned int code, int value); // a function for submitting an absolute coordinate event

Note: After submitting an input device event, you must use the following methods to synchronize the event so that it can inform the input system that the device driver has issued a complete report:

Void input_sync (struct input_dev * dev)

2. Implementation steps of touch screen driver

1. hardware schematic analysis:

The internal touch screen interface of the S3c2440 chip is integrated with the ADC interface. For the hardware structure schematic, see the diagram in the development of the ADC Driver instance on the S3C2440, where Channel 7 (XP or AIN7) X coordinate input for the touch screen interface and Channel 5 (YP or AIN5) as the Y coordinate input for the touch screen interface. In the "Development of ADC Driver instances on S3C2440", the analog signal of the AD conversion is generated by a potentiometer on the Development Board and input through channel 1 (AIN0, the analog signal here is the X coordinate and Y coordinate analog signals generated by the touch screen, which are input through Channel 7 and Channel 5 respectively. The touch screen interface provided by S3c2440 has four processing modes: Normal conversion mode, separate X/Y position conversion mode, automatic X/Y position conversion mode, and waiting for interruption mode, for detailed requirements for working in each mode, see the description of the data manual. The driver instance adopts the automatic X/Y position conversion mode and the waiting for interruption mode.

Note: In each step, in order to make the code logic more organized and easy to understand, the order of the Code is not considered. For example, a function must be defined before calling. To compile this code, adjust the code order strictly in accordance with the C language specifications.

2. Create the touch screen driver my2440_ts.c. First, load and unload the driver. In the driver load part, we mainly do the following: enable the clock, ing IO port, initialization register, applying for interruption, initializing the input device, and registering the input device to the input subsystem required by the ADC. The Code is as follows:

# Include <linux/module. h>
# Include <linux/kernel. h>
# Include <linux/clk. h>
# Include <linux/init. h>
# Include <linux/input. h>
# Include <linux/serio. h>
# Include <plat/regs-adc.h>
# Include <asm/irq. h>
# Include <asm/io. h>

/* Used to save the ADC clock obtained from the platform clock list */
Static struct clk * adc_clk;
/* Defines a memory address used to save the virtual ing */
Static void _ iomem * adc_base;
/* Define an input device to represent our touch screen device */
Static struct input_dev * ts_dev;
/* Device name */
# Define DEVICE_NAME "my2440_TouchScreen"
/* Define a WAIT4INT macro that operates the ADC touch screen control register.
S3C2410_ADCTSC_YM_SEN these macros are defined in regs-adc.h */
# Define WAIT4INT (x) <8) | S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | \
S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_XY_PST (3 ))
Static int _ init ts_init (void)
{
Int ret;
/* Obtain the ADC clock from the platform clock queue. Why is this clock obtained here, because the ADC conversion frequency is related to the clock.
Some clock definitions of the system are in arch/arm/plat-s3c24xx/s3c2410-clock.c */
Adc_clk = clk_get (NULL, "adc ");
If (! Adc_clk)
{
/* Handle errors */
Printk (KERN_ERR "falied to find adc clock source \ n ");
Return-ENOENT;
}
/* The clock can be used after it is obtained, clk_enable is defined in arch/arm/plat-s3c/clock. c */
Clk_enable (adc_clk );
/* Map the IO space occupied by the ADC's IO port to the virtual address of the memory. ioremap is defined in io. h.
Note: The I/O space must be mapped before it can be used. Subsequent operations on virtual addresses are operations on the I/O space,
S3C2410_PA_ADC is the base address of the ADC controller, defined in mach-s3c2410/include/mach/map. h, 0x20 is the virtual address length size */
Adc_base = ioremap (S3C2410_PA_ADC, 0x20 );
If (adc_base = NULL)
{
/* Handle errors */
Printk (KERN_ERR "failed to remap register block \ n ");
Ret =-EINVAL;
Goto err_noclk;
}
/* Initialize the ADC control register and ADC touch screen control register */
Adc_initialize ();
/* Request for ADC interruption. triggered after the adswitch is complete. Here, the shared interrupt IRQF_SHARED is used because the interrupt number is also used in the ADC Driver,
The last parameter 1 is a random value, because if the value is not set to NULL, the application will fail if the value is interrupted */
Ret = request_irq (IRQ_ADC, adc_irq, ir1__shared | ir1__sample_random, DEVICE_NAME, 1 );
If (ret)
{
Printk (KERN_ERR "IRQ % d error % d \ n", IRQ_ADC, ret );
Ret =-EINVAL;
Goto err_nomap;
}
/* Apply for touch screen interruption, triggered when the touch screen is pressed or pen-written */
Ret = request_irq (IRQ_TC, tc_irq, ir1__sample_random, DEVICE_NAME, 1 );
If (ret)
{
Printk (KERN_ERR "IRQ % d error % d \ n", IRQ_TC, ret );
Ret =-EINVAL;
Goto err_noirq;
}
/* Apply for space for the input device. input_allocate_device is defined in input. h */
Ts_dev = input_allocate_device ();
/* Initialize the input device below, that is, set the value for the member of the input device struct input_dev.
The evbit field is used to describe supported events. Synchronization events, key events, and absolute coordinate events are supported here,
BIT macros are actually BIT operations on 1, defined in linux/bitops. h */
Ts_dev-> evbit [0] = BIT (EV_SYN) | BIT (EV_KEY) | BIT (EV_ABS );

/* The keybit field is used to describe the type of the button, which is defined in input. h. BTN_TOUCH is used to represent the touch screen click */
Ts_dev-> keybit [BITS_TO_LONGS (BTN_TOUCH)] = BIT (BTN_TOUCH );
/* For the touch screen, the absolute coordinate system is used. Set the minimum and maximum values of the X and Y coordinates in the coordinate system (range 0)
ABS_X and ABS_Y represent the X and Y coordinates, while ABS_PRESSURE indicates whether the touch screen is pressed or lifted */
Input_set_abs_params (ts_dev, ABS_X, 0, 0x3FF, 0, 0 );
Input_set_abs_params (ts_dev, ABS_Y, 0, 0x3FF, 0, 0 );
Input_set_abs_params (ts_dev, ABS_PRESSURE, 0, 1, 0, 0 );
/* Set the identity information of the touch screen input device, which is directly written here.
This information can be viewed in/proc/bus/input/devices after the driver is mounted */
Ts_dev-> name = DEVICE_NAME;/* device name */
Ts_dev-> id. bustype = BUS_RS232;/* bus type */
Ts_dev-> id. vendor = 0 xDEAD;/* dealer id */
Ts_dev-> id. product = 0 xBEEF;/* product id */
Ts_dev-> id. version = 0x0101;/* version id */
/* Well, some of them are ready. register the ts_dev touch screen device to the input subsystem */
Input_register_device (ts_dev );
Return 0;
/* Handle error redirection below */
Err_noclk:
Clk_disable (adc_clk );
Clk_put (adc_clk );
Err_nomap:
Iounmap (adc_base );
Err_noirq:
Free_irq (IRQ_ADC, 1 );
Return ret;
}
/* Initialize the ADC control register and ADC touch screen control register */
Static void adc_initialize (void)
{
/* The calculation result is (Binary): 111111111000000, which can be found in the Data Manual.
Set the value of the adconverter preset to 255, and enable the adconverter preset to be valid */
Writel (S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL (0xFF), adc_base + S3C2410_ADCCON );
/* Set the ADC start delay register. The delay value is 0xffff */
Writel (0 xffff, adc_base + S3C2410_ADCDLY );
/* The WAIT4INT macro calculation result is (Binary): 11010011, which can be found in the Data Manual.
Set the ADC touch screen control register to the pending interrupt mode */
Writel (WAIT4INT (0), adc_base + S3C2410_ADCTSC );
}
Static void _ exit ts_exit (void)
{
/* Block and release interruptions */
Disable_irq (IRQ_ADC );
Disable_irq (IRQ_TC );
Free_irq (IRQ_ADC, 1 );
Free_irq (IRQ_TC, 1 );
/* Release the virtual address ing space */
Iounmap (adc_base );
/* Shield and destroy the clock */
If (adc_clk)
{
Clk_disable (adc_clk );
Clk_put (adc_clk );
Adc_clk = NULL;
}
/* Remove the touch screen device from the input subsystem */
Input_unregister_device (ts_dev );
}
Module_init (ts_init );
Module_exit (ts_exit );
MODULE_LICENSE ("GPL ");
MODULE_AUTHOR ("Huang Gang ");
MODULE_DESCRIPTION ("My2440 Touch Screen Driver ");

3. The next step is to convert the touch screen status and coordinates in two interrupt service programs. First read the Code as follows:

/* Define an external semaphore ADC_LOCK because ADC_LOCK has been stated in the ADC Driver
This ensures that the ADC resources are mutually exclusive between the ADC Driver and the touch screen driver */
Extern struct semaphore ADC_LOCK;
/* As a tag, X and Y coordinates are converted only after touch screen operations */
Static int OwnADC = 0;
/* Used to record the converted X and Y coordinate values */
Static long xp;
Static long yp;
/* Used to count the number of analog input conversions when the touch screen is pressed down or lifted */
Static int count;
/* Define an AUTOPST macro and set the ADC touch screen control register to automatic conversion mode */
# Define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST (0 ))
/* The touch screen interrupt service program is triggered when the touch screen is pressed or written */
Static irqreturn_t tc_irq (int irq, void * dev_id)
{
/* Used to record the value after this adconversion */
Unsigned long data0;
Unsigned long data1;
/* Used to record whether the touch screen operation status is pressed or lifted */
Int updown;
/* ADC resources can be obtained, that is, locked */
If (down_trylock (& ADC_LOCK) = 0)
{
/* Operation on the touch screen */
OwnADC = 1;
/* Read the value after this adswitch. Note that the main read is the status */
Data0 = readl (adc_base + S3C2410_ADCDAT0 );
Data1 = readl (adc_base + S3C2410_ADCDAT1 );
/* Record whether the touch screen is pressed or lifted this time. The status is stored in the 15th-bit data register, so it is consistent with that of the last S3C2410_ADCDAT0_UPDOWN */
Updown = (! (Data0 & S3C2410_ADCDAT0_UPDOWN ))&&(! (Data1 & S3C2410_ADCDAT0_UPDOWN ));
/* Determine the operation status of the touch screen */
If (updown)
{
/* If it is pressed, call the touch_timer_fire function to start the ADC conversion. The function definition will be discussed later */
Touch_timer_fire (0 );
}
Else
{
/* If it is lifted, this operation is completed, so the possession of ADC resources is released */
OwnADC = 0;
Up (& ADC_LOCK );
}
}
Return IRQ_HANDLED;
}
Static void touch_timer_fire (unsigned long data)
{
/* Used to record the value after this adconversion */
Unsigned long data0;
Unsigned long data1;
/* Used to record whether the touch screen operation status is pressed or lifted */
Int updown;
/* Read the value after this adswitch. Note that the main read is the status */
Data0 = readl (adc_base + S3C2410_ADCDAT0 );
Data1 = readl (adc_base + S3C2410_ADCDAT1 );
/* Record whether the touch screen is pressed or lifted this time. The status is stored in the 15th-bit data register, so it is consistent with that of the last S3C2410_ADCDAT0_UPDOWN */
Updown = (! (Data0 & S3C2410_ADCDAT0_UPDOWN ))&&(! (Data1 & S3C2410_ADCDAT0_UPDOWN ));
/* Determine the operation status of the touch screen */
If (updown)
{
/* Report events and data if the status is pressed and the ADC has been switched */
If (count! = 0)
{
Long tmp;

Tmp = xp;
Xp = yp;
Yp = tmp;

Xp >>=2;
Yp> = 2;
# Ifdef CONFIG_TOUCHSCREEN_MY2440_DEBUG
/* Touch Screen debugging information. After this option is selected during kernel compilation, click the touch screen to print the coordinate information on the terminal */
Struct timeval TV;
Do_gettimeofday (& TV );
Printk (KERN_DEBUG "T: % 06d, X: % 03ld, Y: % 03ld \ n", (int) TV. TV _usec, xp, yp );
# Endif
/* Report the absolute coordinate values of X and Y */
Input_report_abs (ts_dev, ABS_X, xp );
Input_report_abs (ts_dev, ABS_Y, yp );
/* Report the status of the touch screen. 1 indicates that the touch screen is pressed */
Input_report_abs (ts_dev, ABS_PRESSURE, 1 );
/* Report the key event. The key value is 1 (indicating that the key corresponding to the touch screen is pressed )*/
Input_report_key (ts_dev, BTN_TOUCH, 1 );
/* Wait for the recipient to receive the data and send a response for confirmation */
Input_sync (ts_dev );
}
/* If the status is pressed and the ADC has not started conversion, start the ADC for conversion */
Xp = 0;
Yp = 0;
Count = 0;
/* Set the touch screen mode to automatic conversion mode */
Writel (S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, adc_base + S3C2410_ADCTSC );
/* Start ADC conversion */
Writel (readl (adc_base + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base + S3C2410_ADCCON );
}
Else
{
/* Otherwise, it is in the lift status */
Count = 0;
/* Report the key event. The key value is 0 (indicating that the button corresponding to the touch screen is released )*/
Input_report_key (ts_dev, BTN_TOUCH, 0 );
/* Report the status of the touch screen. 0 indicates that the touch screen is not pressed */
Input_report_abs (ts_dev, ABS_PRESSURE, 0 );
/* Wait for the recipient to receive the data and send a response for confirmation */
Input_sync (ts_dev );
/* Reset the touch screen to the waiting for interruption status */
Writel (WAIT4INT (0), adc_base + S3C2410_ADCTSC );
/* If the touch screen is lifted, this operation is completed, so the possession of ADC resources is released */
If (OwnADC)
{
OwnADC = 0;
Up (& ADC_LOCK );
}
}
}
/* Define and initialize a timer touch_timer. The timer service program is touch_timer_fire */
Static struct timer_list touch_timer = TIMER_INITIALIZER (touch_timer_fire, 0, 0 );
/* The ADC interrupts the service program and triggers execution after the adswitch is complete */
Static irqreturn_t adc_irq (int irq, void * dev_id)
{
/* Used to record the value after this adconversion */
Unsigned long data0;
Unsigned long data1;
If (OwnADC)
{
/* Read the value after this adconversion. Note that the coordinates are read this time */
Data0 = readl (adc_base + S3C2410_ADCDAT0 );
Data1 = readl (adc_base + S3C2410_ADCDAT1 );
/* Record the X and Y coordinate values after the AD conversion. According to the data manual, the X and Y coordinate values are converted.
The values are stored in the 0-9 bits of the Data Registers 0 and 1 respectively. Therefore, the value of the 0-9 bits is used with the value of the last S3C2410_ADCDAT0_XPDATA_MASK */
Xp + = data0 & S3C2410_ADCDAT0_XPDATA_MASK;
Yp + = data1 & S3C2410_ADCDAT1_YPDATA_MASK;
/* Count the number of times this adconversion was performed */
Count ++;
If (count <(1 <2 ))
{
/* If the number of conversions is less than 4, restart the ADC conversion */
Writel (S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, adc_base + S3C2410_ADCTSC );
Writel (readl (adc_base + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base + S3C2410_ADCCON );
}
Else
{
/* Otherwise, start a time-tick timer, which will execute the timer service program to report events and Data */
Mod_timer (& touch_timer, jiffies + 1 );
Writel (WAIT4INT (1), adc_base + S3C2410_ADCTSC );
}
}
Return IRQ_HANDLED;
}

The process of conversion is described as follows:
(1) If the touch screen is touched, the touch screen interruption is triggered to enter tc_irq. After obtaining ADC_LOCK and judging that the touch screen status is pressed, touch_timer_fire is called to start the ADC conversion;
(2) When the ADC conversion starts, the ADC interruption is triggered to enter adc_irq. If the number of conversions is less than 4, restart the ADC for conversion. If the conversion is completed four times, start a time tick timer to stop the ADC conversion. That is to say, the ADC conversion stops during this time tick;
(3) Why do we need to stop the ADC conversion before one tick answer arrives? This is to prevent screen jitter.
(4) If one tick arrives, the system enters the timer service program touch_timer_fire. If the touch screen is still pressed, the system reports the event and converted data, restarts the ADC conversion, and repeats step (2) step;
(5) If the touch is lifted, the release event is reported and the touch screen is reset to the waiting for interruption status.

4. porting and testing touch screen drivers

Porting and testing please see Linux-2.6.30.4 on 2440 for porting touch screen drive

If ($! = JQuery) {$ = jQuery. noConflict ();} var isLogined = false; var cb_blogId = 77585; var cb_entryId = 1994233; var cb_blogApp = "hoys"; var cb_blogUserGuid = "Callback "; var cb_entryCreatedDate = '2017/24 18:46:00 ';

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.