Function: lp_write_buf copies a buffer of characters to the parallel device.
Called from: lp_write.
Files: drivers/char/lp.c, include/linux/lp.h
lp_write_buf has three arguments: minor, the minor device number of this parallel port, buf, the buffer in which the data to be sent to the device is held and count, the number of characters to be sent.
static inline int lp_write_buf(unsigned int minor, const char *buf, int count) { unsigned long copy_size; unsigned long total_bytes_written = 0; unsigned long bytes_written; struct lp_struct *lp = & lp_table[minor]; unsigned char status;
A couple of reality checks, to begin with. Ensure that the minor number being used isn't higher than the number of parallel devices that the kernel has actually been configured to cope with (LP_NO). In the code that this was taken from, LP_NO was set to 3, although this is, of course, arbitrary. Then make sure that the device that we have asked for has actually been detected by the kernel at all.
if (minor >= LP_NO) return -ENXIO; if (lp_table[minor].dev == NULL) return -ENXIO;
Begin the loop that copies the data from user space to the device. Determine the amount of characters that will be copied from user space in one go - either use LP_BUFFER_SIZE, or copy all the characters in one shot, if the number is less than LP_BUFFER_SIZE.
do { bytes_written = 0; copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); copy_from_user(lp->lp_buffer, buf, copy_size);
Now send the characters one by one into lp_char, checking the return value each time.
while (copy_size) { if (lp_char(lp->lp_buffer[bytes_written], minor, must_use_polling(minor))) { --copy_size; ++bytes_written; lp_table[minor].runchars++;
If lp_char returned with an error, get the status from the parallel port, and log an appropriate message.
} else { int rc = total_bytes_written + bytes_written; if (lp_table[minor].runchars > LP_STAT(minor).maxrun) LP_STAT(minor).maxrun = lp_table[minor].runchars; status = r_str(minor);
Out of paper:
if ((status & LP_POUTPA)) { printk(KERN_INFO "lp%d out of paper\n", minor); if (LP_F(minor) & LP_ABORT) return rc ? rc : -ENOSPC; lp_error(minor);
Printer offline:
} else if (!(status & LP_PSELECD)) { printk(KERN_INFO "lp%d off-line\n", minor); if (LP_F(minor) & LP_ABORT) return rc ? rc : -EIO; lp_error(minor);
Some other error:
} else if (!(status & LP_PERRORP)) { printk(KERN_ERR "lp%d printer error\n", minor); if (LP_F(minor) & LP_ABORT) return rc ? rc : -EIO; lp_error(minor); }
Now we sleep for a short while (I'm not entirely sure why here -- perhaps someone would kindly let me know). The first case is for where we're using polling. I really don't know what's going on here. Russell Coker (rjc@virtual.net.au) suggested that this might just be for scheduling - allowing the kernel to go do something else at this point.
LP_STAT(minor).sleeps++; if (must_use_polling(minor)) { #ifdef LP_DEBUG printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp_table[minor].runchars, LP_TIME(minor)); #endif lp_table[minor].runchars = 0; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + LP_TIME(minor); lp_schedule (minor);
And the second is for where we're using interrupts. Again, I'm not too sure what's happening here.
} else { cli(); enable_irq(lp->dev->port->irq); w_ctr(minor, LP_PSELECP|LP_PINITP|LP_PINTEN); status = r_str(minor); if ((!(status & LP_PACK) || (status & LP_PBUSY)) && LP_CAREFUL_READY(minor, status)) { w_ctr(minor, LP_PSELECP | LP_PINITP); sti(); continue; } lp_table[minor].runchars = 0; current->timeout = jiffies + LP_TIMEOUT_INTERRUPT; interruptible_sleep_on(& lp->lp_wait_q); w_ctr(minor, LP_PSELECP | LP_PINITP); sti(); }
If a signal is pending, then clean up and leave.
if (signal_pending(current)) { if (total_bytes_written + bytes_written) return total_bytes_written + bytes_written; else return -EINTR; } } }
Update the counters and go back through the loop again, until we've sent every character.
total_bytes_written += bytes_written; buf += bytes_written; count -= bytes_written; } while (count > 0); return total_bytes_written; }