short驱动详解

转自:http://blog.sina.com.cn/s/blog_5f84dc840100o4u4.html


/ *
After you compile it, when you run the shell scriptshort_load, maybe you'll encounter the following error:
insmod: error inserting './short.ko': -1 No such device.
If you have a look inside the short_load script, you will findthis file 'short.ko' does exist. There is no error in it.
If you run dmesg(or more /var/log/messages), you will find thecause.
'short: can't get I/O port address0x378' 
Some other module has already occupied thisport. 
Run the command 'more /proc/ioports', you will find that0378-037a is occupied by parport.
You must unload them using following commands in the sameorder:
#rmmod lp
#rmmod parport_pc
#rmmod parport
Then you can run short_load correctly.
I inserted one LED to pin 2 and groundpin. But no matter whatever I input, the LED light always keptlighting. I don't know where iswrong. 
* /

/ *
 * short.c -- Simple Hardware Operations and RawTests
 * short.c -- also a brief example of interrupthandling ("short int")
 *
 * Copyright (C) 2001 Alessandro Rubini andJonathan Corbet
 * Copyright (C) 2001 O'Reilly &Associates
 *
 * The source code in this file can be freelyused, adapted,
 * and redistributed in source or binary form, solong as an
 * acknowledgment appears in derived sourcefiles.  The citation
 * should list that the code comes from the book"Linux Device
 * Drivers" by Alessandro Rubini and JonathanCorbet, published
 * by O'Reilly &Associates.   No warranty isattached;
 * we cannot take responsibility for errors orfitness for use.
 *
 * $Id: short.c,v 1.16 2004/10/29 16:45:40 corbetExp $
 * /

/ *
 * FIXME: this driver is not safe with concurrentreaders or
 * writers.
 * /

#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include<linux/kernel.h>   / * printk() * /
#include<linux/fs.h>       / * everything... * /
#include <linux/errno.h> / * errorcodes * /
#include <linux/delay.h> / * udelay */
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/poll.h>
#include <linux/wait.h>

#include <asm/io.h>

#define SHORT_NR_PORTS 8   / *use 8 ports by default * /

/ *
 * all of the parameters have no "short_" prefix,to save typing when
 * specifying them at load time
 * /
static int major = 0;   / *dynamic by default * /
module_param(major, int, 0);

static int use_mem = 0; / * default is I/O-mapped * /
module_param(use_mem, int, 0);

/ * default is the first printer port on PC's. "short_base" isthere too
   because it's what we want touse in the code * /
static unsigned long base = 0x378;
unsigned long short_base = 0;
module_param(base, long, 0);

/ * The interrupt line is undefined by default. "short_irq" isas above * /
static int irq = -1;
volatile int short_irq = -1;
module_param(irq, int, 0);

static int probe = 0;   / *select at load time how to probe irq line * /
module_param(probe, int, 0);

static int wq = 0;  / * select at load timewhether a workqueue is used * /
module_param(wq, int, 0);

static int tasklet = 0; / * select whether a tasklet is used */
module_param(tasklet, int, 0);

static int share = 0;   / *select at load time whether install a shared irq * /
module_param(share, int, 0);

MODULE_AUTHOR ("Alessandro Rubini");
MODULE_LICENSE("Dual BSD/GPL");

unsigned long short_buffer = 0;
unsigned long volatile short_head;
volatile unsigned long short_tail;
DECLARE_WAIT_QUEUE_HEAD(short_queue);

/ * Set up our tasklet if we're doing that. * /
void short_do_tasklet(unsigned long);
DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0);

/ *
 * Atomicly increment an index intoshort_buffer
 * /
static inline void short_incr_bp(volatile unsigned long *index, intdelta)
{
    unsignedlong new = *index + delta;

   barrier();  / * Don't optimize these two together* /
    *index =(new >= (short_buffer + PAGE_SIZE)) ? short_buffer :new;
}

/ *
 * The devices with low minor numbers write/readburst of data to/from
 * specific I/O ports (by default the parallelones).
 *
 * The device with 128 as minor number returnsascii strings telling
 * when interrupts have been received. Writing tothe device toggles
 * 00/FF on the parallel data lines. If there is aloopback wire, this
 * generates interrupts.
 * /

int short_open (struct inode *inode, struct file *filp)
{
    externstruct file_operations short_i_fops;

    / * ifminor device number is greater than or equal to 128, then useinterrupt-driven node* /

    / * So tospeak, /dev/shortint or /dev/shortprint * / 

    if(iminor (inode) & 0x80)
    {
       filp->f_op = &short_i_fops;
                                / * the interrupt-driven node * /
    return0;
}

int short_release (struct inode *inode, struct file *filp)
{
    return0;
}

/ * first, the port-oriented device * /

enum short_modes {
   SHORT_DEFAULT = 0, SHORT_PAUSE, SHORT_STRING, SHORT_MEMORY
};

ssize_t do_short_read (struct inode *inode, struct file *filp,char __user *buf,
                      size_t count, loff_t *f_pos)
{
    int retval =count, minor = iminor (inode);
    unsignedlong port = short_base + (minor & 0x0f);
    void*address = (void *) short_base + (minor &0x0f);
    int mode =(minor & 0x70) >>4;
    unsignedchar *kbuf = kmalloc(count, GFP_KERNEL), *ptr;

    if(!kbuf)
    {
       return -ENOMEM;
    }

    ptr =kbuf;

    if(use_mem)
    {
       mode = SHORT_MEMORY;
    }

    switch(mode)
    {
    caseSHORT_STRING:
       insb(port, ptr, count);
       rmb();
       break;

    caseSHORT_DEFAULT:
       while (count--)
       {
           *(ptr++) = inb(port);
           rmb();
       }

       break;

    caseSHORT_MEMORY:
       while (count--)
       {
           *ptr++ = ioread8(address);
           rmb();
       }

       break;
    caseSHORT_PAUSE:
       while (count--)
       {
           *(ptr++) = inb_p(port);
           rmb();
       }

       break;

   default:    / * no more modes defined by now * /
       retval = -EINVAL;
       break;
    }

    if((retval > 0) &&copy_to_user(buf, kbuf, retval))
    {
       retval = -EFAULT;
    }

   kfree(kbuf);
    returnretval;
}

/ *
 * Version-specific methods for the fopsstructure.  FIXME don't need anymore.
 * /
ssize_t short_read(struct file *filp, char __user *buf, size_tcount, loff_t *f_pos)
{
    returndo_short_read(filp->f_dentry->d_inode,filp, buf, count, f_pos);
}

ssize_t do_short_write (struct inode *inode, struct file *filp,const char __user *buf,
                       size_t count, loff_t *f_pos)
{
    int retval =count, minor = iminor(inode);
    unsignedlong port = short_base + (minor & 0x0f);
    void*address = (void *) short_base + (minor &0x0f);
    int mode =(minor & 0x70) >>4;
    unsignedchar *kbuf = kmalloc(count, GFP_KERNEL), *ptr;

    if(!kbuf)
    {
       return -ENOMEM;
    }

    if(copy_from_user(kbuf, buf, count)) / *从用户空间拷贝数据到内核* /
    {
       return -EFAULT;
    }

    ptr =kbuf;

    if(use_mem)
    {
       mode = SHORT_MEMORY;
    }

    switch(mode)
    {
    caseSHORT_PAUSE:
       while (count--)
       {
           outb_p(*(ptr++), port);
           wmb();
       }

       break;

    caseSHORT_STRING:
       outsb(port, ptr, count);
       wmb();
       break;

    caseSHORT_DEFAULT:  / *对应于设备/dev/short0* /
       while (count--)
       {
           outb(*(ptr++), port);  / *一次向port写入一个字节8位* /
           wmb();
       }

       break;

    caseSHORT_MEMORY:
       while (count--)
       {
           iowrite8(*ptr++, address);
           wmb();
       }

       break;

    default:/ * no more modes defined by now * /
       retval = -EINVAL;
       break;
    }

   kfree(kbuf);
    returnretval;
}

ssize_t short_write(struct file *filp, const char __user *buf,size_t count,
                   loff_t *f_pos)
{
    returndo_short_write(filp->f_dentry->d_inode,filp, buf, count, f_pos);
}

unsigned int short_poll(struct file *filp, poll_table*wait)
{
    returnPOLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}

struct file_operations short_fops =
{
   .owner   = THIS_MODULE,
   .read    =short_read,
   .write   = short_write,
   .poll    =short_poll,   / * 对应select操作* /
   .open    =short_open,
    .release =short_release,
};

/ * then,  the interrupt-related device * /

ssize_t short_i_read (struct file *filp, char __user *buf,size_t count, loff_t *f_pos)
{
    intcount0;


   DEFINE_WAIT(wait);

    while(short_head == short_tail) / * circular buffer is empty.* /
    {
       prepare_to_wait(&short_queue,&wait, TASK_INTERRUPTIBLE);
       if (short_head == short_tail)
       {
           schedule();
       }

       finish_wait(&short_queue,&wait);

       / *signal_pending( current)―――》检查当前进程是否有信号处理,返回不为0表示有信号需要处理。-ERESTARTSYS表示信号函数处理完毕后重新执行信号函数前的某个系统调用。也就是说,如果信号函数前有发生系统调用,在调度用户信号函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.如果返回值-ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。*/

       if (signal_pending (current))
       {
           / * a signal arrived * /
           return -ERESTARTSYS;
                             / * tell the fs layer to handle it * /
    }

    / *count0 is the number of readable data bytes * /
    count0 =short_head - short_tail;
    if (count0< 0)
    {
       / * wrapped * /
       count0 = short_buffer + PAGE_SIZE - short_tail;
    }

    if(count0 < count)
    {
       count = count0;
    }

    if(copy_to_user(buf, (char *)short_tail, count))
    {
       return -EFAULT;
    }

   short_incr_bp (&short_tail, count); / *移动写指针*/
    returncount;
}

ssize_t short_i_write (struct file *filp, const char __user*buf, size_t count,
                      loff_t *f_pos)
{
    int written= 0, odd = *f_pos & 1;
    unsignedlong port = short_base; / * output to the parallel data latch */
    void*address = (void *) short_base;

    if(use_mem)
    {
       while (written < count)
       {
           iowrite8(0xff * ((++written + odd) & 1),address);
       }
    }
    else
    {
       while (written < count)
       {
           outb(0xff * ((++written + odd) & 1), port);
       }
    }

    *f_pos +=count;
    returnwritten;
}

struct file_operations short_i_fops =
{
   .owner   = THIS_MODULE,
   .read    =short_i_read,
   .write   = short_i_write,
   .open    =short_open,
    .release =short_release,
};

irqreturn_t short_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
    structtimeval tv;
    intwritten;

   do_gettimeofday(&tv);

    / * Writea 16 byte record. Assume PAGE_SIZE is a multiple of 16 * /
    written =sprintf((char *)short_head, "u.u\n",
                     (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
   BUG_ON(written != 16);
   short_incr_bp(&short_head, written);
   wake_up_interruptible(&short_queue); / * awake anyreading process * /
    returnIRQ_HANDLED;
}

/ *
 * The following two functions are equivalent tothe previous one,
 * but split in top and bottom half. First, a fewneeded variables
 * /

#define NR_TIMEVAL 512 / * length of the array of time values */

struct timeval tv_data[NR_TIMEVAL]; / * too lazy to allocate it* / / *用于tasklet的另一个circular buffer,用于临时存储中间数据.* /
volatile struct timeval *tv_head = tv_data;
volatile struct timeval *tv_tail = tv_data;

static struct work_struct short_wq;

int short_wq_count = 0;

/ *
 * Increment a circular buffer pointer in a waythat nobody sees
 * an intermediate value.
 * /
static inline void short_incr_tv(volatile struct timeval**tvp)
{
    if (*tvp ==(tv_data + NR_TIMEVAL - 1))
    {
       *tvp = tv_data;
                     / * Wrap * /
    else
    {
       (*tvp)++;
    }
}

void short_do_tasklet (unsigned long unused)
{
    intsavecount = short_wq_count, written;

   short_wq_count = 0; / * we have already been removed from the queue* /

    / *
    * The bottom half reads the tv array, filled by the top half,
    * and prints it to the circular text buffer, which is thenconsumed
    * by reading processes
    * /

    / * Firstwrite the number of interrupts that occurred before this bh */
    written =sprintf((char *)short_head, "bh after %6i\n", savecount);
   short_incr_bp(&short_head, written);

    / *
    * Then, write the time values. Write exactly 16 bytes at atime,
    * so it aligns with PAGE_SIZE
    * /

    do
    {
       written = sprintf((char *)short_head, "u.u\n",
                         (int)(tv_tail->tv_sec % 100000000),
                         (int)(tv_tail->tv_usec));
       short_incr_bp(&short_head, written);
       short_incr_tv(&tv_tail);
    } while(tv_tail != tv_head);

   wake_up_interruptible(&short_queue); / * awake anyreading process * /
}

irqreturn_t short_wq_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
    / * Grab thecurrent time information. * /
   do_gettimeofday((struct timeval *) tv_head);
   short_incr_tv(&tv_head);

    / * Queuethe bh. Don't worry about multiple enqueueing * /
   schedule_work(&short_wq);

   short_wq_count++; / * record that an interrupt arrived * /
    returnIRQ_HANDLED;
}

/ *
 * Tasklet top half
 * /

irqreturn_t short_tl_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
   do_gettimeofday((struct timeval *) tv_head); / * cast to stop'volatile' warning * /
   short_incr_tv(&tv_head);
   tasklet_schedule(&short_tasklet);
   short_wq_count++; / * record that an interrupt arrived * /
    returnIRQ_HANDLED;
}

irqreturn_t short_sh_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
    int value,written;
    structtimeval tv;

    / * If itwasn't short, return immediately * /
    value =inb(short_base);
    if (!(value& 0x80))
    {
       return IRQ_NONE;
    }

    / * clearthe interrupting bit * /
    outb(value& 0x7F, short_base);

    / * therest is unchanged * /

   do_gettimeofday(&tv);
    written =sprintf((char *)short_head, "u.u\n",
                     (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
   short_incr_bp(&short_head, written); / *修改circularbuffer 中的写指针* /
   wake_up_interruptible(&short_queue); / * awake anyreading process * /
    returnIRQ_HANDLED;
}

void short_kernelprobe(void)
{
    int count =0;

    do
    {
       unsigned long mask;

       mask = probe_irq_on();
       outb_p(0x10, short_base + 2); / * enable reporting * /
       outb_p(0x00, short_base);   / *clear the bit * /
       outb_p(0xFF, short_base);   / *set the bit: interrupt! * /
       outb_p(0x00, short_base + 2); / * disable reporting * /
       udelay(5);  / * give it some time * /
       short_irq = probe_irq_off(mask);

       if (short_irq == 0)
       {
           / * none of them? * /
           printk(KERN_INFO "short: no irq reported by probe\n");
           short_irq = -1;
       }

       / *
        * if more than one line has been activated, the result is
        * negative. We should service the interrupt (no need for lptport)
        * and loop over again. Loop at most five times, then give up
        * /
    } while((short_irq < 0) &&(count++ < 5));

    if(short_irq < 0)
    {
       printk("short: probe failed %i times, giving up\n", count);
    }
}

irqreturn_t short_probing(int irq, void *dev_id, struct pt_regs*regs)
{
    if(short_irq == 0)
    {
       short_irq = irq;
                                        / * found * /
    if(short_irq != irq)
    {
       short_irq = -irq;
                                        / * ambiguous * /
    returnIRQ_HANDLED;
}

void short_selfprobe(void)
{
    int trials[]= {3, 5, 7, 9, 0};
    int tried[]= {0, 0, 0, 0, 0};
    int i, count= 0;

    / *
    * install the probing handler for all possible lines.Remember
    * the result (0 for success, or -EBUSY) in order to only free
    * what has been acquired
    * /
    for (i = 0;trials[i]; i++)
    {
       tried[i] = request_irq(trials[i], short_probing,
                              SA_INTERRUPT, "short probe", NULL);
    }

    do
    {
       short_irq = 0; / * none got, yet * /
       outb_p(0x10, short_base + 2); / * enable * /
       outb_p(0x00, short_base);
       outb_p(0xFF, short_base); / * toggle the bit * /
       outb_p(0x00, short_base + 2); / * disable * /
       udelay(5);  / * give it some time * /

       / * the value has been set by the handler * /
       if (short_irq == 0)
       {
           / * none of them? * /
           printk(KERN_INFO "short: no irq reported by probe\n");
       }

       / *
        * If more than one line has been activated, the result is
        * negative. We should service the interrupt (but the lpt port
        * doesn't need it) and loop over again. Do it at most 5 times
        * /
    } while((short_irq <= 0) &&(count++ < 5));

    / * endof loop, uninstall the handler * /
    for (i = 0;trials[i]; i++)
    {
       if (tried[i] == 0)
       {
           free_irq(trials[i], NULL);
       }
    }

    if(short_irq < 0)
    {
       printk("short: probe failed %i times, giving up\n", count);
    }
}

/ *

   void* __ioremap(unsigned long phys_addr, unsigned long size, unsignedlong flags)
  void*ioremap(unsigned long phys_addr, unsigned longsize)

  入口: phys_addr:要映射的起始的IO地址;   size:要映射的空间的大小;   flags:要映射的IO空间的和权限有关的标志;   phys_addr:是要映射的物理地址,   size:是要映射的长度,   S3C2410的long是32位而非你说的64位。   功能:将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问;   实现:对要映射的IO地址空间进行判断,低PCI/ISA地址不需要重新映射,也不允许用户将IO地址空间映射到正在使用的RAM中,最后申请一个vm_area_struct结构,调用remap_area_pages填写页表,若填写过程不成功则释放申请的vm_area_struct空间;   ioremap 依靠__ioremap实现,它只是在__ioremap中以第三个参数为0调用来实现.   ioremap是内核提供的用来映射外设寄存器到主存的函数,我们要映射的地址已经从pci_dev中读了出来(上一步),这样就水到渠成的成功映射了而不会和其他地址有冲突。映射完了有什么效果呢,我举个例子,比如某个网卡有100个寄存器,他们都是连在一块的,位置是固定的,假如每个寄存器占4个字节,那么一共400个字节的空间被映射到内存成功后,ioaddr就是这段地址的开头(注意ioaddr是虚拟地址,而mmio_start是物理地址,它是BIOS得到的,肯定是物理地址,而保护模式下CPU不认物理地址,只认虚拟地址),ioaddr+0就是第一个寄存器的地址,ioaddr+4就是第二个寄存器地址(每个寄存器占4个字节),以此类推,我们就能够在内存中访问到所有的寄存器进而操控他们了。

   void request_region(unsigned long from, unsigned long num, const char *name) 

   这个函数用来申请一块输入输出区域。如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。 

  参数1:io端口的基地址。 

  参数2:io端口占用的范围。 

  参数3:使用这段io地址的设备名。 

  在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。 

   request_region()用于内核为驱动“分配”端口,这里分配的意思是,记录该端口已经被某个进程使用,要是其它进程试图访问它,就会产生“忙”错误。所以目的在于实现资源的互斥访问。 

   反之,如果一个资源只被一个进程访问,不会导致资源的争用,这时request_region()是可选的。

   释放使用  voidrelease_region(unsigned long from, unsigned long num) 

   几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)。而具体采用哪一种则取决于CPU的体系结构。

   io-map的寄存器用outb/w/l,inb/w/l指令访问

   mem-map的寄存器直接用指针赋值即可,但是由于对内存访问可能被gcc优化掉,所以最好使用linux提供的包装函数:ioread8,ioread16,ioread32,iowrite8,iowrite16,iowrite32(早期版本为readb/w/l,writeb/w/l)。

   io-map也有相应的包装函数/宏。

* /

/ * Finally, init and cleanup * /

int short_init(void)
{
    intresult;

    / *
    * first, sort out the base/short_base ambiguity: we'd better
    * use short_base in the code, for clarity, but allow setting
    * just "base" at load time. Same for "irq".
    * /
    short_base =base;
    short_irq =irq;

    / * Getour needed resources. * /
    if(!use_mem) / * 对于IO mapped的IOport的操作,使用 request_region*/

    {
       if (!request_region(short_base,SHORT_NR_PORTS, "short"))  / * IO mapped* /
       {
           printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",
                  short_base);
           return -ENODEV;
       }
    }
    else/ *对于mem mapped的IOpoart操作,使用request_mem_region及 ioremap */

    {
       if (!request_mem_region(short_base, SHORT_NR_PORTS, "short"))/ * Mem Mapped* /
       {
           printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n",
                  short_base);
           return -ENODEV;
       }

       / * also, ioremap it * /
       short_base = (unsigned long) ioremap(short_base, SHORT_NR_PORTS);

       / * Hmm... we should check the return value * /
    }

    / * Herewe register our device - should not fail thereafter * /

    / *Inprevious code, register_chrdev_region is used. These two functionsare similar.* /

    / *

 

    1.register_chrdev注册字符设备后,有0-255个子设备可用,若major==0,则内核动态申请主设备号。

    staticinline int register_chrdev(unsigned int major, const char *name,const 

               struct file_operations *fops)

   2.register_chrdev_region注册字符设备后,有count个子设备号可用(从from开始),dev_t from =MKDEV(major,minor)

   int register_chrdev_region(dev_t from,unsigned count, const char *name)

 

    * /
    result =register_chrdev(major, "short", &short_fops);
    if (result< 0)
    {
       printk(KERN_INFO "short: can't get major number\n");
       release_region(short_base, SHORT_NR_PORTS);  / *FIXME - use-mem case? * /
       return result;
    }

    if (major== 0)
    {
       major = result;
                                / * dynamic * /

    /*分配一页内存* /

   short_buffer = __get_free_pages(GFP_KERNEL, 0); / * never fails */  / * FIXME * /
    short_head =short_tail = short_buffer;  / *使用circular buffer*/

    / *
    * Fill the workqueue structure, used for the bottom halfhandler.
    * The cast is there to prevent warnings about the type of the
    * (unused) argument.
    * /

    / * thisline is in short_init() * /

    /*工作队列的初始化。从2.6.20的内核开始,INIT_WORK宏做了改变,原来是三个参数,后来改成了两个参数* /
   //INIT_WORK(&short_wq, (void(*)(void *))short_do_tasklet, NULL); 

   INIT_WORK(&short_wq, (void(*)(void *))short_do_tasklet);

    / *
    * Now we deal with the interrupt: either kernel-based
    * autodetection, DIY detection or default number
    * /

   /*使用内核提供的探测函数:probe_irq_on和probe_irq_off* /

    if((short_irq < 0) &&(probe == 1))  
    {
       short_kernelprobe();
    }

   /*不使用内核提供的中断探测函数,自主探测* /

    if((short_irq < 0) &&(probe == 2))
    {
       short_selfprobe();
    }

    /*如果探测失败,或者用户没有要求自动探测,使用默认值* /

    if(short_irq < 0)
    {
       / * not yet specified: force the default on * /
       switch (short_base)
       {
       case 0x378:
           short_irq = 7;
           break;
       case 0x278:
           short_irq = 2;
           break;
       case 0x3bc:
           short_irq = 5;
           break;
       }
    }

    / *
    * If shared has been specified, installed the shared handler
    * instead of the normal one. Do it first, before a -EBUSYwill
    * force short_irq to -1.
    * /

    /*安装共享中断shared interrupt* /
    if((short_irq >= 0) &&(share > 0))
    {
       result = request_irq(short_irq, short_sh_interrupt,
                            SA_SHIRQ | SA_INTERRUPT, "short",
                            short_sh_interrupt);
       if (result)
       {
           printk(KERN_INFO "short: can't get assigned irq %i\n",short_irq);
           short_irq = -1;
       }
       else
       {
           / * actually enable it -- assume this *is* a parallel port */
           outb(0x10, short_base + 2);
       }

       return 0; / * the rest of the function only installs handlers */
    }

   / *安装独享中断non-shared interrupt* /

    if(short_irq >= 0)
    {
       result = request_irq(short_irq, short_interrupt,
                            SA_INTERRUPT, "short", NULL);
       if (result)
       {
           printk(KERN_INFO "short: can't get assigned irq %i\n",
                  short_irq);
           short_irq = -1;
       }
       else
       {
           / * actually enable it -- assume this *is* a parallel port */
           outb(0x10, short_base + 2);
       }
    }

    / *
    * Ok, now change the interrupt handler if using top/bottomhalves
    * has been requested
    * /

    /*安装workqueue和tasklet interrupt handler* /
    if((short_irq >= 0) &&(wq + tasklet) > 0)
    {
       free_irq(short_irq, NULL);
       result = request_irq(short_irq,
                            tasklet ? short_tl_interrupt :
                            short_wq_interrupt,
                            SA_INTERRUPT, "short-bh", NULL);
       if (result)
       {
           printk(KERN_INFO "short-bh: can't get assigned irq %i\n",
                  short_irq);
           short_irq = -1;
       }
    }

    return0;
}

void short_cleanup(void)
{

    / *释放中断*/
    if(short_irq >= 0)
    {
       outb(0x0, short_base + 2);   / *disable the interrupt * /
       if (!share)
       {
           free_irq(short_irq, NULL);
       }
       else
       {
           free_irq(short_irq, short_sh_interrupt);
       }
    }

    / * Makesure we don't leave work queue/tasklet functions running * /
    if(tasklet)
    {
       tasklet_disable(&short_tasklet);
    }
    else
    {
       flush_scheduled_work();
    }

   unregister_chrdev(major, "short");
    if(use_mem)
    {
       iounmap((void __iomem *)short_base);
       release_mem_region(short_base, SHORT_NR_PORTS);
    }
    else
    {
       release_region(short_base, SHORT_NR_PORTS);
    }

    if(short_buffer)
    {
       free_page(short_buffer);
    }
}

module_init(short_init);
module_exit(short_cleanup);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值