Linux keyboard driver analysis

  
 The keyboard is the simplest of all the drivers, but it contains the basic framework of the driver. It will be of great benefit to further study other complex drivers in the future. The following is a step-by-step analysis of driver development. The query method is adopted. 1. Kernel module registration and revocation
When loading the module, the kernel module registration function is first run. Its functions include kernel registration of devices and initialization of variables.
static int head,tail;
int _init Keypad_init(void)
{
int result;
result=register_chrdev(KEY_LED_MAJOR,KEY_LED_NAME,&Keypad_fops);
Keypad_clear() ;
init_waitqueue_head(&queue);
prink("%s %s initialized.\ ",KEY_LED_NAME,KEY_LED_VERSION);//Cannot use prinf
return 0;
}
module_init(Keypad_init);//Load module
void _exit Keypad_cleanup(void)
{
del_timer(&timer);
unregister_chrdev(KEY_LED_MAJOR,KEY_LED_NAME);
prink(" Keypad driver removed \ ");
}
module_exit(Keypad_cleanup);//Uninstall the module
2.Virtual file system and hardware driver interface
static struct file_operations Keypad_fops={
Open:Keypad_open,
read:Keypad_read,
poll:Keypad_poll,
fasync:Keypad_fasync,
release:Keypad_release,
};
After the interface is defined, some are The implementation of several specific functions! Now let's move on to the next step. Do you think it's actually not difficult? Don't be so happy early? When these functions are implemented, many technologies are involved, including kernel timers, * specific implementations of waiting queues (blocking mode), specific implementation techniques of asynchronous methods, and circular queues. Are you excited to see so many technologies? I will explain it to you in a common way. I hope you can understand it.
(More information welcome to login: qiangren.blog.edu.cn)
Three. Device open operation interface function implementation (Keypad_open)
Device open generally includes two major operations, one is to complete the device Initialization, the second is the device reference counter plus 1
static int Keypad_open(struct inode *inode,struct file *filp)
{
read_xy();
try_module_get(THIS_MODULE);//This function Added for the linux 2.6 kernel, unlike the 2.4 kernel, the function is the value of the counter plus 1
return 0;
}
static void read_xy(void)
{
new_data();//Get key-value function
keypad_starttimer();//Enable kernel timer to get new keyboard changes in fixed cycle time
}
The following implementation keyboard key value acquisition function read_xy()
main It is from KEY_CS (corresponding read address, which can be defined according to specific hardware devices, such as #define kEY_CS(*(volatile unsigned short *)(0xf820000)). It should be different according to the specifics!
Read The entered key value is stored in the buf[] cache. The write pointer of the ring buffer is head, and the read pointer is tail.
///////////////////////////////////keyboard event data structure definition /////////////////////////////////////
typedef struct{
ulong status;//The value of the button
ulong click;//Whether there is a button press, 1 means yes, 0 means no
}KEY_EVENT
static KEY_EVENT cur_data,buf[BUFSIZE];//BUFSIZE is a macro definition, used to define the size of the ring buffer
static void new_data (void)
{
if((KEY_CS & 0xff)!=0xff) //Read data from the KEY_CS address. If there is a 0, it means that a button has been pressed (here the hardware circuit Is active low)
{
switch(KEY_CS & 0xff){
case ~KEY0 & 0xff:
cur_data.status=1;///////1 is pressed under
break;

case ~ KEY1 & 0xff:
cur_data.status = 2; //2 is pressed
break;
/////////Others added, understand? ?
}
cur_data.click=1;
}
else if(KEY_CS & 0xff==0xff){
cur_data.click=0;
cur_data.status=0;
}
if(head!=tail){////////The application of the circular queue buffer starts here ^_^
int last=head--;
if (last<0)////////If you have reached the heading, skip to the end of the team to implement the loop queue
last=BUFSIZE-1;
}
//////Key information is stored in the circular queue buffer
buf[head]=cur_data;
if(++head==BUFSIZE)
head=0;
if(head==tail & ;& tail++=BUFSIZE)
tail=0;
if(fasync)
kill_fasync(&fasyc,SIGIO,POLL_IN);
wake_up_interruptible(&queue);
}
today to write to you first, the following sections will be launched, so stay tuned

Next we introduce several other file interface functions to achieve
IV. first introduced shutdown function keypad_release (), Why Introduce it first? The reason is very simple, it should be relatively simple, let everyone do the warm-up exercise, after introducing this, continue to introduce a more complicated function, see if you can eat it.
The main operation of the shutdown operation is: turn off the device asynchronously. Notification, device counter minus 1, delete timer signal interrupt
static int Keypad_release(struct inode *inode,struct)
{
Keypad_fasync(-1,filp,0);
module_put(THIS_MODULE) ;
del_timer(&timer);
return 0;
}
5. Device read operation interface function implementation Keypad_read()
The main function is to read the key value from the buffer. By calling get_data(), copy the key value to the user's data area via the copy_to_user() function
static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)
{< Br>DECLEARE_WAITQUEUE(wait,current);//Declare a wait queue, add the current process to the wait queue
KEY_EVENT t;
ulong out_buf[2];
if(head==tail)//No data in the current circular queue can be read
{
if(filp->f_flags & O_NONBLOCK)//If the user Use non-blocking mode to read
return _EAGAIN;
add_wait_queue(&queue,&wait);//Add the current process to the waiting queue
current->state=TASK_INTERRUPTIBLE;//Set the current The state of the process
while((head==tail)&&!signal_pending(current))//if there is no data to the loop queue and the current process is not affected by the signal
{
shedule(); //Process scheduling
current->state=TASK_INTERRUPTIBLE;
}
current->state=TASK_RUNNING;
remove_wait_queue(&queue,&wait);
if(head ==tail)
return count;
t=get_data();//Invoke the get_data() function to get the data in the buffer. The following is a detailed introduction
out_buf[0]=t. Status;
out_buf[1]=t.click;
copy_to_user(buf,&out_buf,sizeof(out_buf));//Copy the obtained key value to the user data area
return count;< Br>
}
}
It is natural that we should introduce the implementation of the get_data() function. The function of this function is to read the key values ​​we want from the circular queue buffer we defined. So it’s actually very simple if you understand The principle of the ring queue, there is no more explanation here, you should have the general knowledge of data structure
static KEY_EVENT get_data(void)
{
int last=tail
if(++tail ==BUFSIZE)
tail=0;
return buf[last];
}
If you can understand the above, then you can enter the following study, mainly introduces the kernel timer Use, use the wait queue to implement blocking I /O, poll system call, asynchronous notification mode, after the introduction, I will give an application instance, for the use of the file operating system
call For the keyboard drivers we write, they are basically the same. Let's talk nonsense, we will start our exciting drive development right away!
Copyright © Windows knowledge All Rights Reserved