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

📄 short.c

📁 Linux设备驱动的经典教材, 该电子书是第三版,并附有全部配套代码.
💻 C
📖 第 1 页 / 共 2 页
字号:
	wake_up_interruptible(&short_queue); /* awake any reading process */	return IRQ_HANDLED;}/* * The following two functions are equivalent to the previous one, * but split in top and bottom half. First, a few needed variables */#define NR_TIMEVAL 512 /* length of the array of time values */struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */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 way that 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){	int savecount = 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 then consumed	 * by reading processes	 */	/* First write 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 a time,	 * so it aligns with PAGE_SIZE	 */	do {		written = sprintf((char *)short_head,"%08u.%06u\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 any reading process */}irqreturn_t short_wq_interrupt(int irq, void *dev_id, struct pt_regs *regs){	/* Grab the current time information. */	do_gettimeofday((struct timeval *) tv_head);	short_incr_tv(&tv_head);	/* Queue the bh. Don't worry about multiple enqueueing */	schedule_work(&short_wq);	short_wq_count++; /* record that an interrupt arrived */	return IRQ_HANDLED;}/* * Tasklet top half */irqreturn_t short_tl_interrupt(int irq, void *dev_id, struct pt_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 */	return IRQ_HANDLED;}irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs){	int value, written;	struct timeval tv;	/* If it wasn't short, return immediately */	value = inb(short_base);	if (!(value & 0x80))		return IRQ_NONE;		/* clear the interrupting bit */	outb(value & 0x7F, short_base);	/* the rest is unchanged */	do_gettimeofday(&tv);	written = sprintf((char *)short_head,"%08u.%06u\n",			(int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));	short_incr_bp(&short_head, written);	wake_up_interruptible(&short_queue); /* awake any reading process */	return IRQ_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 lpt port)		 * 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 */	return IRQ_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);	/* end of 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);}/* Finally, init and cleanup */int short_init(void){	int result;	/*	 * 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;	/* Get our needed resources. */	if (!use_mem) {		if (! request_region(short_base, SHORT_NR_PORTS, "short")) {			printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",					short_base);			return -ENODEV;		}	} else {		if (! request_mem_region(short_base, SHORT_NR_PORTS, "short")) {			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 */	}	/* Here we register our device - should not fail thereafter */	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;	/*	 * Fill the workqueue structure, used for the bottom half handler.	 * The cast is there to prevent warnings about the type of the	 * (unused) argument.	 */	/* this line is in short_init() */	INIT_WORK(&short_wq, (void (*)(void *)) short_do_tasklet, NULL);	/*	 * Now we deal with the interrupt: either kernel-based	 * autodetection, DIY detection or default number	 */	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 -EBUSY will	 * force short_irq to -1.	 */	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 */	}	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/bottom halves	 * has been requested	 */	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;		}	}	return 0;}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);	}	/* Make sure 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);

⌨️ 快捷键说明

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