📄 shortprint.c
字号:
* with ours.
*/
if (down_interruptible (&shortp_out_sem))
return -ERESTARTSYS;
/*
* Out with the data.
*/
while (written < count) {
/* Hang out until some buffer space is available. */
space = shortp_out_space();
if (space <= 0) {
if (wait_event_interruptible(shortp_out_queue,
(space = shortp_out_space()) > 0)) {
*f_pos += written;
up(&shortp_out_sem);
return -ERESTARTSYS;
}
}
/* Move data into the buffer. */
if ((space + written) > count)
space = count - written;
if (copy_from_user((char *) shortp_out_head, buf, space)) {
up(&shortp_out_sem);
return -EFAULT;
}
shortp_incr_out_bp(&shortp_out_head, space);
buf += space;
written += space;
/* If no output is active, make it active. */
spin_lock_irqsave(&shortp_out_lock, flags);
if (! shortp_output_active)
shortp_do_write ();
spin_unlock_irqrestore(&shortp_out_lock, flags);
}
*f_pos += written;
up(&shortp_out_sem);
return written;
}
/*
* The bottom-half handler.
*/
static struct tq_struct shortp_task;
void shortp_do_task (void *unused)
{
int written;
unsigned long flags;
/* Keep the output going */
spin_lock_irqsave(&shortp_out_lock, flags);
if (shortp_out_head == shortp_out_tail) { /* empty */
shortp_output_active = 0;
wake_up_interruptible(&shortp_empty_queue);
del_timer_sync(&shortp_timer);
}
else
shortp_do_write();
/* If somebody's waiting, wake them up. */
if (((PAGE_SIZE + shortp_out_tail - shortp_out_head) % PAGE_SIZE) > SP_MIN_SPACE) {
wake_up_interruptible(&shortp_out_queue);
}
spin_unlock_irqrestore(&shortp_out_lock, flags);
/* Handle the "read" side operation */
written = sprintf((char *)shortp_in_head, "%08u.%06u\n",
(int)(shortp_tv.tv_sec % 100000000),
(int)(shortp_tv.tv_usec));
shortp_incr_bp(&shortp_in_head, written);
wake_up_interruptible(&shortp_in_queue); /* awake any reading process */
}
/*
* The top-half interrupt handler.
*/
void shortp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
if (! shortp_output_active)
printk(KERN_INFO "shortprint: spurious interrupt\n");
/* Remember the time, and farm off the rest to the task queue function */
do_gettimeofday(&shortp_tv);
// schedule_task(&shortp_task);
queue_task(&shortp_task, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
/*
* Interrupt timeouts. Just because we got a timeout doesn't mean that
* things have gone wrong, however; printers can spend an awful long time
* just thinking about things.
*/
static void shortp_timeout(unsigned long unused)
{
unsigned long flags;
unsigned char status;
spin_lock_irqsave(&shortp_out_lock, flags);
status = inb(shortp_base + SP_STATUS);
/* If the printer is still busy we just reset the timer */
if ((status & SP_SR_BUSY) == 0 || (status & SP_SR_ACK)) {
shortp_timer.expires = jiffies + TIMEOUT;
add_timer(&shortp_timer);
spin_unlock_irqrestore(&shortp_out_lock, flags);
return;
}
/* Otherwise we must have dropped an interrupt. */
spin_unlock_irqrestore(&shortp_out_lock, flags);
shortp_interrupt (shortp_irq, NULL, NULL);
}
/*
* 2.0 wrappers.
*/
#ifdef LINUX_20
int shortp_read_20 (struct inode *inode, struct file *filp, char *buf,
int count)
{
return shortp_read (filp, buf, count, &filp->f_pos);
}
int shortp_write_20 (struct inode *inode, struct file *filp, const char *buf,
int count)
{
return shortp_write (filp, buf, count, &filp->f_pos);
}
void shortp_release_20 (struct inode *inode, struct file *filp)
{
(void) shortp_release(inode, filp);
}
#define shortp_read shortp_read_20
#define shortp_write shortp_write_20
#define shortp_release shortp_release_20
#endif /* LINUX_20 */
struct file_operations shortp_fops = {
read: shortp_read,
write: shortp_write,
open: shortp_open,
release: shortp_release,
/* should really implement poll too */
};
/*
* Module initialization
*/
int shortp_init(void)
{
int result;
/*
* first, sort out the base/shortp_base ambiguity: we'd better
* use shortp_base in the code, for clarity, but allow setting
* just "base" at load time. Same for "irq".
*/
shortp_base = base;
shortp_irq = irq;
shortp_delay = delay;
/* Set up owner pointers.*/
SET_MODULE_OWNER(&shortp_fops);
/* Get our needed resources. */
result = check_region(shortp_base, SP_NPORTS);
if (result) {
printk(KERN_INFO "shortprint: can't get I/O port address 0x%lx\n",
shortp_base);
return result;
}
request_region(shortp_base, SHORTP_NR_PORTS, "shortprint");
/* Register the device */
result = register_chrdev(major, "shortprint", &shortp_fops);
if (result < 0) {
printk(KERN_INFO "shortp: can't get major number\n");
release_region(shortp_base,SHORTP_NR_PORTS);
return result;
}
if (major == 0) major = result; /* dynamic */
/* Initialize the input buffer. */
shortp_in_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */
shortp_in_head = shortp_in_tail = shortp_in_buffer;
/* And the output buffer. */
shortp_out_buffer = (unsigned char *) __get_free_pages(GFP_KERNEL, 0);
shortp_out_head = shortp_out_tail = shortp_out_buffer;
sema_init (&shortp_out_sem, 1);
/* And the output info */
shortp_output_active = 0;
spin_lock_init (&shortp_out_lock);
init_timer(&shortp_timer);
shortp_timer.function = shortp_timeout;
shortp_timer.data = 0;
/* Fill the shortp_task structure, used for the bottom half handler. */
shortp_task.routine = shortp_do_task;
shortp_task.data = NULL; /* unused */
/* If no IRQ was explicitly requested, pick a default */
if (shortp_irq < 0)
switch(shortp_base) {
case 0x378: shortp_irq = 7; break;
case 0x278: shortp_irq = 2; break;
case 0x3bc: shortp_irq = 5; break;
}
/* Request the IRQ */
result = request_irq(shortp_irq, shortp_interrupt, 0, "shortprint", NULL);
if (result) {
printk(KERN_INFO "shortprint: can't get assigned irq %i\n",
shortp_irq);
shortp_irq = -1;
shortp_cleanup ();
return result;
}
/* Initialize the control register, turning on interrupts. */
outb (SP_CR_IRQ | SP_CR_SELECT | SP_CR_INIT, shortp_base + SP_CONTROL);
return 0;
}
void shortp_cleanup(void)
{
/* Return the IRQ if we have one */
if (shortp_irq >= 0) {
outb(0x0, shortp_base + SP_CONTROL); /* disable the interrupt */
free_irq(shortp_irq, NULL);
}
/* Don't leave any timers floating around. Note that any active output
is effectively stopped by turning off the interrupt */
if (shortp_output_active)
del_timer_sync (&shortp_timer);
/* All done with the device */
unregister_chrdev(major, "shortprint");
release_region(shortp_base,SHORTP_NR_PORTS);
if (shortp_in_buffer) free_page(shortp_in_buffer);
}
module_init(shortp_init);
module_exit(shortp_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -