📄 shortprint.c
字号:
/*
* A version of the "short" driver which drives a parallel printer directly,
* with a lot of simplifying assumptions.
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
* $Id: shortprint.c,v 1.4 2001/07/18 22:28:18 rubini Exp $
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/delay.h> /* udelay */
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/tqueue.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
#include "sysdep.h"
#include "shortprint.h"
#define SHORTP_NR_PORTS 3
/*
* all of the parameters have no "shortp_" prefix, to save typing when
* specifying them at load time
*/
static int major = 0; /* dynamic by default */
MODULE_PARM(major, "i");
/* default is the first printer port on PC's. "shortp_base" is there too
because it's what we want to use in the code */
static unsigned long base = 0x378;
unsigned long shortp_base = 0;
MODULE_PARM(base, "l");
/* The interrupt line is undefined by default. "shortp_irq" is as above */
static int irq = -1;
static int shortp_irq = -1;
MODULE_PARM(irq, "i");
/* Microsecond delay around strobe. */
static int delay = 0;
static int shortp_delay;
MODULE_PARM(delay, "i");
MODULE_AUTHOR ("Jonathan Corbet");
/*
* Forwards.
*/
void shortp_cleanup ();
static void shortp_timeout (unsigned long unused);
/*
* Input is managed through a simple circular buffer which, among other things,
* is allowed to overrun if the reader isn't fast enough. That makes life simple
* on the "read" interrupt side, where we don't want to block.
*/
static unsigned long shortp_in_buffer = 0;
static unsigned long volatile shortp_in_head;
static volatile unsigned long shortp_in_tail;
DECLARE_WAIT_QUEUE_HEAD(shortp_in_queue);
static struct timeval shortp_tv; /* When the interrupt happened. */
/*
* Atomicly increment an index into shortp_in_buffer
*/
static inline void shortp_incr_bp(volatile unsigned long *index, int delta)
{
unsigned long new = *index + delta;
barrier (); /* Don't optimize these two together */
*index = (new >= (shortp_in_buffer + PAGE_SIZE)) ? shortp_in_buffer : new;
}
/*
* On the write side we have to be more careful, since we don't want to drop
* data. The semaphore is used to serialize write-side access to the buffer;
* there is only one consumer, so read-side access is unregulated. The
* wait queue will be awakened when space becomes available in the buffer.
*/
unsigned char *shortp_out_buffer = NULL;
volatile unsigned char *shortp_out_head, *shortp_out_tail;
struct semaphore shortp_out_sem;
DECLARE_WAIT_QUEUE_HEAD (shortp_out_queue);
/*
* Available space in the output buffer; should be called with the semaphore
* held. Returns contiguous space, so caller need not worry about wraps.
*/
static inline int shortp_out_space()
{
if (shortp_out_head >= shortp_out_tail) {
int space = PAGE_SIZE - (shortp_out_head - shortp_out_buffer);
return (shortp_out_tail == shortp_out_buffer) ? space - 1 : space;
}
else
return (shortp_out_tail - shortp_out_head) - 1;
}
static inline void shortp_incr_out_bp(volatile unsigned char **bp, int incr)
{
unsigned char *new = (unsigned char *) *bp + incr;
if (new >= (shortp_out_buffer + PAGE_SIZE))
new -= PAGE_SIZE;
*bp = new;
}
/*
* The output "process" is controlled by a spin lock; decisions on
* shortp_output_active or manipulation of shortp_out_tail require
* that this lock be held.
*/
static spinlock_t shortp_out_lock;
volatile static int shortp_output_active;
DECLARE_WAIT_QUEUE_HEAD (shortp_empty_queue); /* waked when queue empties */
static int nwrote = 0;
/*
* When output is active, the timer is too, in case we miss interrupts. Hold
* shortp_out_lock if you mess with the timer.
*/
static struct timer_list shortp_timer;
#define TIMEOUT 5*HZ /* Wait a long time */
/*
* Open the device.
*/
int shortp_open (struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
return 0;
}
int shortp_release (struct inode *inode, struct file *filp)
{
/* Wait for any pending output to complete */
wait_event_interruptible(shortp_empty_queue, shortp_output_active==0);
MOD_DEC_USE_COUNT;
return 0;
}
#ifdef __USE_OLD_SELECT__
int shortp_poll (struct inode *inode, struct file *filp,
int mode, select_table *table)
{
return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */
}
#define poll select
#else /* Use poll */
unsigned int shortp_poll(struct file *filp, poll_table *wait)
{
return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}
#endif /* __USE_OLD_SELECT__ */
/*
* The read routine, which doesn't return data from the device; instead, it
* returns timing information just like the "short" device.
*/
ssize_t shortp_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int count0;
while (shortp_in_head == shortp_in_tail) {
interruptible_sleep_on(&shortp_in_queue);
if (signal_pending (current)) /* a signal arrived */
return -ERESTARTSYS; /* tell the fs layer to handle it */
/* else, loop */
}
/* count0 is the number of readable data bytes */
count0 = shortp_in_head - shortp_in_tail;
if (count0 < 0) /* wrapped */
count0 = shortp_in_buffer + PAGE_SIZE - shortp_in_tail;
if (count0 < count) count = count0;
if (copy_to_user(buf, (char *)shortp_in_tail, count))
return -EFAULT;
shortp_incr_bp (&shortp_in_tail, count);
return count;
}
/*
* Write the next character from the buffer. There should *be* a next
* character... The spinlock should be held when this routine is called.
*/
static void shortp_do_write()
{
unsigned char cr = inb(shortp_base + SP_CONTROL);
/* Make sure the device is ready for us */
if ((inb(shortp_base + SP_STATUS) & SP_SR_BUSY) == 0) {
// printk (KERN_INFO "shortprint: waiting for printer busy\n");
// printk (KERN_INFO "Status is 0x%x\n", inb(shortp_base + SP_STATUS));
while ((inb(shortp_base + SP_STATUS) & SP_SR_BUSY) == 0) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10*HZ);
}
}
/* Mark output active and start up our timer if need be */
if (! shortp_output_active) {
shortp_output_active = 1;
shortp_timer.expires = jiffies + TIMEOUT;
add_timer (&shortp_timer);
}
else
mod_timer(&shortp_timer, jiffies + TIMEOUT);
/* Strobe a byte out to the device */
outb_p(*shortp_out_tail, shortp_base+SP_DATA);
shortp_incr_out_bp(&shortp_out_tail, 1);
if (shortp_delay) udelay(shortp_delay);
outb_p(cr | SP_CR_STROBE, shortp_base+SP_CONTROL);
if (shortp_delay) udelay(shortp_delay);
outb_p(cr & ~SP_CR_STROBE, shortp_base+SP_CONTROL);
nwrote++;
}
/*
* Write to the device.
*/
ssize_t shortp_write (struct file *filp, const char *buf, size_t count,
loff_t *f_pos)
{
int space, written = 0;
unsigned long flags;
/*
* Take and hold the semaphore for the entire duration of the operation. The
* consumer side ignores it, and it will keep other data from interleaving
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -