⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mousedrivers.tmpl

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 TMPL
📖 第 1 页 / 共 3 页
字号:
  <para>    We can fill in the write handler at this point as the write function for     our mouse simply declines to allow writes:  </para>  <programlisting>static ssize_t write_mouse(struct file *file, const char *buffer, size_t                                count, loff_t *ppos){        return -EINVAL;}  </programlisting>  <para>    This is pretty much self-explanatory. Whenever you write you get told     it was an invalid function.  </para>  <para>    To make the poll and read functions work we have to consider how we     handle the mouse interrupt.   </para>  <programlisting>static struct wait_queue *mouse_wait;static spinlock_t mouse_lock = SPIN_LOCK_UNLOCKED;static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs){        char delta_x;        char delta_y;        unsigned char new_buttons;        delta_x = inb(OURMOUSE_BASE);        delta_y = inb(OURMOUSE_BASE+1);        new_buttons = inb(OURMOUSE_BASE+2);        if(delta_x || delta_y || new_buttons != mouse_buttons)        {                /* Something happened */                spin_lock(&amp;mouse_lock);                mouse_event = 1;                mouse_dx += delta_x;                mouse_dy += delta_y;                mouse_buttons = new_buttons;                spin_unlock(&amp;mouse_lock);                                wake_up_interruptible(&amp;mouse_wait);        }}  </programlisting>  <para>    The interrupt handler reads the mouse status. The next thing we do is     to check whether something has changed. If the mouse was smart it would    only interrupt us if something had changed, but let's assume our mouse     is stupid as most mice actually tend to be.   </para>  <para>    If the mouse has changed we need to update the status variables. What we    don't want is the mouse functions reading these variables to read them    during a change. We add a spinlock that protects these variables while we    play with them.  </para>  <para>    If a change has occurred we also need to wake sleeping processes, so we     add a wakeup call and a <structname>wait_queue</structname> to use when     we wish to await a mouse event.  </para>  <para>    Now we have the wait queue we can implement the poll function for the     mouse relatively easily:  </para>  <programlisting>static unsigned int mouse_poll(struct file *file, poll_table *wait){        poll_wait(file, &amp;mouse_wait, wait);        if(mouse_event)                return POLLIN | POLLRDNORM;        return 0;}  </programlisting>  <para>    This is fairly standard poll code. First we add the wait queue to the     list of queues we want to monitor for an event. Secondly we check if an     event has occurred. We only have one kind of event - the     <varname>mouse_event</varname> flag tells us that something happened.     We know that this something can only be mouse data. We return the flags     indicating input and normal reading will succeed.  </para>  <para>    You may be wondering what happens if the function returns saying 'no     event yet'. In this case the wake up from the wait queue we added to     the poll table will cause the function to be called again. Eventually     we will be woken up and have an event ready. At this point the     <function>poll</function> call will exit back to the user.  </para>  <para>    After the poll completes the user will want to read the data. We now     need to think about how our <function>mouse_read</function> function     will work:  </para>  <programlisting>static ssize_t mouse_read(struct file *file, char *buffer,                 size_t count, loff_t *pos){        int dx, dy;        unsigned char button;        unsigned long flags;        int n;        if(count&lt;3)                return -EINVAL;        /*          *        Wait for an event         */        while(!mouse_event)        {                if(file-&gt;f_flags&amp;O_NDELAY)                        return -EAGAIN;                interruptible_sleep_on(&amp;mouse_wait);                if(signal_pending(current))                        return -ERESTARTSYS;        }  </programlisting>  <para>    We start by validating that the user is reading enough data. We could     handle partial reads if we wanted but it isn't terribly useful and the     mouse drivers don't bother to try.  </para>  <para>    Next we wait for an event to occur. The loop is fairly standard event    waiting in Linux. Having checked that the event has not yet occurred, we    then check if an event is pending and if not we need to sleep.   </para>  <para>    A user process can set the <constant>O_NDELAY</constant> flag on a file     to indicate that it wishes to be told immediately if no event is     pending. We check this and give the appropriate error if so.   </para>  <para>    Next we sleep until the mouse or a signal awakens us. A signal will     awaken us as we have used <function>wakeup_interruptible</function>.     This is important as it means a user can kill processes waiting for     the mouse - clearly a desirable property. If we are interrupted we     exit the call and the kernel will then process signals and maybe     restart the call again - from the beginning.  </para>  <para>    This code contains a classic Linux bug. All will be revealed later in this    article as well as explanations for how to avoid it.  </para>  <programlisting>        /* Grab the event */        spinlock_irqsave(&amp;mouse_lock, flags);        dx = mouse_dx;        dy = mouse_dy;        button = mouse_buttons;        if(dx&lt;=-127)                dx=-127;        if(dx&gt;=127)                dx=127;        if(dy&lt;=-127)                dy=-127;        if(dy&gt;=127)                dy=127;        mouse_dx -= dx;        mouse_dy -= dy;                if(mouse_dx == 0 &amp;&amp; mouse_dy == 0)                mouse_event = 0;        spin_unlock_irqrestore(&amp;mouse_lock, flags);  </programlisting>  <para>    This is the next stage. Having established that there is an event     going, we capture it. To be sure that the event is not being updated     as we capture it we also take the spinlock and thus prevent parallel     updates. Note here we use <function>spinlock_irqsave</function>. We     need to disable interrupts on the local processor otherwise bad things     will happen.  </para>  <para>    What will occur is that we take the spinlock. While we hold the lock     an interrupt will occur. At this point our interrupt handler will try     and take the spinlock. It will sit in a loop waiting for the read     routine to release the lock. However because we are sitting in a loop     in the interrupt handler we will never release the lock. The machine     hangs and the user gets upset.  </para>  <para>    By blocking the interrupt on this processor we ensure that the lock     holder will always give the lock back without deadlocking.  </para>  <para>    There is a little cleverness in the reporting mechanism too. We can     only report a move of 127 per read. We don't however want to lose     information by throwing away further movement. Instead we keep     returning as much information as possible. Each time we return a     report we remove the amount from the pending movement in     <varname>mouse_dx</varname> and <varname>mouse_dy</varname>. Eventually     when these counts hit zero we clear the <varname>mouse_event</varname>    flag as there is nothing else left to report.  </para>  <programlisting>        if(put_user(button|0x80, buffer))                return -EFAULT;        if(put_user((char)dx, buffer+1))                return -EFAULT;        if(put_user((char)dy, buffer+2))                return -EFAULT;        for(n=3; n < count; n++)                if(put_user(0x00, buffer+n))                        return -EFAULT;        return count;}  </programlisting>  <para>    Finally we must put the results in the user supplied buffer. We cannot     do this while holding the lock as a write to user memory may sleep.     For example the user memory may be residing on disk at this instant.     Thus we did our computation beforehand and now copy the data. Each     <function>put_user call</function> is filling in one byte of the buffer.     If it returns an error we inform the program that it passed us an     invalid buffer and abort.  </para>  <para>    Having written the data we blank the rest of the buffer that was read     and report the read as being successful.  </para> </chapter> <chapter id="debugging">  <title>Debugging the mouse driver</title>  <para>    We now have an almost perfectly usable mouse driver. If you were to     actually try and use it however you would eventually find a couple of     problems with it. A few programs will also not work with as it does not     yet support asynchronous I/O.  </para>  <para>    First let us look at the bugs. The most obvious one isn't really a driver    bug but a failure to consider the consequences. Imagine you bumped the     mouse hard by accident and sent it skittering across the desk. The mouse     interrupt routine will add up all that movement and report it in steps of     127 until it has reported all of it. Clearly there is a point beyond     which mouse movement isn't worth reporting. We need to add this as a     limit to the interrupt handler:  </para>  <programlisting>static void ourmouse_interrupt(int irq, void *dev_id, struct pt_regs *regs){        char delta_x;        char delta_y;        unsigned char new_buttons;        delta_x = inb(OURMOUSE_BASE);        delta_y = inb(OURMOUSE_BASE+1);        new_buttons = inb(OURMOUSE_BASE+2);        if(delta_x || delta_y || new_buttons != mouse_buttons)        {                /* Something happened */                spin_lock(&amp;mouse_lock);                mouse_event = 1;                mouse_dx += delta_x;                mouse_dy += delta_y;                if(mouse_dx &lt; -4096)                        mouse_dx = -4096;                if(mouse_dx &gt; 4096)                        mouse_dx = 4096;                if(mouse_dy &lt; -4096)                        mouse_dy = -4096;                if(mouse_dy &gt; 4096)                        mouse_dy = 4096;                mouse_buttons = new_buttons;                spin_unlock(&amp;mouse_lock);                                wake_up_interruptible(&amp;mouse_wait);        }}  </programlisting>  <para>    By adding these checks we limit the range of accumulated movement to    something sensible.   </para>  <para>    The second bug is a bit more subtle, and that is perhaps why this is     such a common mistake. Remember, I said the waiting loop for the read     handler had a bug in it. Think about what happens when we execute:  </para>  <programlisting>        while(!mouse_event)        {  </programlisting>  <para>    and an interrupt occurs at this point here. This causes a mouse movement    and wakes up the queue.   </para>  <programlisting>                interruptible_sleep_on(&amp;mouse_wait);  </programlisting>  <para>    Now we sleep on the queue. We missed the wake up and the application     will not see an event until the next mouse event occurs. This will     lead to just the odd instance when a mouse button gets delayed. The     consequences to the user will probably be almost undetectable with a     mouse driver. With other drivers this bug could be a lot more severe.  </para>  <para>    There are two ways to solve this. The first is to disable interrupts     during the testing and the sleep. This works because when a task sleeps     it ceases to disable interrupts, and when it resumes it disables them     again. Our code thus becomes:  </para>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -