您的位置:首页 > 其它

short驱动详解

2016-04-15 20:01 267 查看
转自: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) &©_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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: