📄 printer.c
字号:
usb_free_urb(usblp->readurb);
kfree (usblp);
}
static void usblp_unlink_urbs(struct usblp *usblp)
{
usb_unlink_urb(usblp->writeurb);
if (usblp->bidir)
usb_unlink_urb(usblp->readurb);
}
static int usblp_release(struct inode *inode, struct file *file)
{
struct usblp *usblp = file->private_data;
down (&usblp->sem);
lock_kernel();
usblp->used = 0;
if (usblp->dev) {
usblp_unlink_urbs(usblp);
up(&usblp->sem);
} else /* finish cleanup from disconnect */
usblp_cleanup (usblp);
unlock_kernel();
return 0;
}
/* No kernel lock - fine */
static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait)
{
struct usblp *usblp = file->private_data;
poll_wait(file, &usblp->wait, wait);
return ((!usblp->bidir || usblp->readurb->status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM)
| (usblp->writeurb->status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM);
}
static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct usblp *usblp = file->private_data;
int length, err, i;
unsigned char status, newChannel;
int twoints[2];
int retval = 0;
down (&usblp->sem);
if (!usblp->dev) {
retval = -ENODEV;
goto done;
}
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
switch (_IOC_NR(cmd)) {
case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
if (_IOC_DIR(cmd) != _IOC_READ) {
retval = -EINVAL;
goto done;
}
length = usblp_cache_device_id_string(usblp);
if (length < 0) {
retval = length;
goto done;
}
if (length > _IOC_SIZE(cmd))
length = _IOC_SIZE(cmd); /* truncate */
if (copy_to_user((unsigned char *) arg,
usblp->device_id_string,
(unsigned long) length)) {
retval = -EFAULT;
goto done;
}
break;
case IOCNR_GET_PROTOCOLS:
if (_IOC_DIR(cmd) != _IOC_READ ||
_IOC_SIZE(cmd) < sizeof(twoints)) {
retval = -EINVAL;
goto done;
}
twoints[0] = usblp->current_protocol;
twoints[1] = 0;
for (i = USBLP_FIRST_PROTOCOL;
i <= USBLP_LAST_PROTOCOL; i++) {
if (usblp->protocol[i].alt_setting >= 0)
twoints[1] |= (1<<i);
}
if (copy_to_user((unsigned char *)arg,
(unsigned char *)twoints,
sizeof(twoints))) {
retval = -EFAULT;
goto done;
}
break;
case IOCNR_SET_PROTOCOL:
if (_IOC_DIR(cmd) != _IOC_WRITE) {
retval = -EINVAL;
goto done;
}
#ifdef DEBUG
if (arg == -10) {
usblp_dump(usblp);
break;
}
#endif
usblp_unlink_urbs(usblp);
retval = usblp_set_protocol(usblp, arg);
if (retval < 0) {
usblp_set_protocol(usblp,
usblp->current_protocol);
}
break;
case IOCNR_HP_SET_CHANNEL:
if (_IOC_DIR(cmd) != _IOC_WRITE ||
usblp->dev->descriptor.idVendor != 0x03F0 ||
usblp->quirks & USBLP_QUIRK_BIDIR) {
retval = -EINVAL;
goto done;
}
err = usblp_hp_channel_change_request(usblp,
arg, &newChannel);
if (err < 0) {
err("usblp%d: error = %d setting "
"HP channel",
usblp->minor, err);
retval = -EIO;
goto done;
}
dbg("usblp%d requested/got HP channel %ld/%d",
usblp->minor, arg, newChannel);
break;
case IOCNR_GET_BUS_ADDRESS:
if (_IOC_DIR(cmd) != _IOC_READ ||
_IOC_SIZE(cmd) < sizeof(twoints)) {
retval = -EINVAL;
goto done;
}
twoints[0] = usblp->dev->bus->busnum;
twoints[1] = usblp->dev->devnum;
if (copy_to_user((unsigned char *)arg,
(unsigned char *)twoints,
sizeof(twoints))) {
retval = -EFAULT;
goto done;
}
dbg("usblp%d is bus=%d, device=%d",
usblp->minor, twoints[0], twoints[1]);
break;
case IOCNR_GET_VID_PID:
if (_IOC_DIR(cmd) != _IOC_READ ||
_IOC_SIZE(cmd) < sizeof(twoints)) {
retval = -EINVAL;
goto done;
}
twoints[0] = usblp->dev->descriptor.idVendor;
twoints[1] = usblp->dev->descriptor.idProduct;
if (copy_to_user((unsigned char *)arg,
(unsigned char *)twoints,
sizeof(twoints))) {
retval = -EFAULT;
goto done;
}
dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
usblp->minor, twoints[0], twoints[1]);
break;
default:
retval = -EINVAL;
}
else /* old-style ioctl value */
switch (cmd) {
case LPGETSTATUS:
if (usblp_read_status(usblp, &status)) {
err("usblp%d: failed reading printer status", usblp->minor);
retval = -EIO;
goto done;
}
if (copy_to_user ((unsigned char *)arg, &status, 1))
retval = -EFAULT;
break;
default:
retval = -EINVAL;
}
done:
up (&usblp->sem);
return retval;
}
static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
struct usblp *usblp = file->private_data;
int timeout, err = 0, writecount = 0;
while (writecount < count) {
if (!usblp->wcomplete) {
barrier();
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
timeout = USBLP_WRITE_TIMEOUT;
add_wait_queue(&usblp->wait, &wait);
while ( 1==1 ) {
if (signal_pending(current)) {
remove_wait_queue(&usblp->wait, &wait);
return writecount ? writecount : -EINTR;
}
set_current_state(TASK_INTERRUPTIBLE);
if (timeout && !usblp->wcomplete) {
timeout = schedule_timeout(timeout);
} else {
set_current_state(TASK_RUNNING);
break;
}
}
remove_wait_queue(&usblp->wait, &wait);
}
down (&usblp->sem);
if (!usblp->dev) {
up (&usblp->sem);
return -ENODEV;
}
if (usblp->writeurb->status != 0) {
if (usblp->quirks & USBLP_QUIRK_BIDIR) {
if (!usblp->wcomplete)
err("usblp%d: error %d writing to printer",
usblp->minor, usblp->writeurb->status);
err = usblp->writeurb->status;
} else
err = usblp_check_status(usblp, err);
up (&usblp->sem);
/* if the fault was due to disconnect, let khubd's
* call to usblp_disconnect() grab usblp->sem ...
*/
schedule ();
continue;
}
writecount += usblp->writeurb->transfer_buffer_length;
usblp->writeurb->transfer_buffer_length = 0;
if (writecount == count) {
up (&usblp->sem);
break;
}
usblp->writeurb->transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ?
(count - writecount) : USBLP_BUF_SIZE;
if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount,
usblp->writeurb->transfer_buffer_length)) return -EFAULT;
usblp->writeurb->dev = usblp->dev;
usblp->wcomplete = 0;
if (usb_submit_urb(usblp->writeurb, GFP_KERNEL)) {
count = -EIO;
up (&usblp->sem);
break;
}
up (&usblp->sem);
}
return count;
}
static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usblp *usblp = file->private_data;
DECLARE_WAITQUEUE(wait, current);
if (!usblp->bidir)
return -EINVAL;
down (&usblp->sem);
if (!usblp->dev) {
count = -ENODEV;
goto done;
}
if (!usblp->rcomplete) {
barrier();
if (file->f_flags & O_NONBLOCK) {
count = -EAGAIN;
goto done;
}
// FIXME: only use urb->status inside completion
// callbacks; this way is racey...
add_wait_queue(&usblp->wait, &wait);
while (1==1) {
if (signal_pending(current)) {
count = -EINTR;
remove_wait_queue(&usblp->wait, &wait);
goto done;
}
up (&usblp->sem);
set_current_state(TASK_INTERRUPTIBLE);
if (!usblp->rcomplete) {
schedule();
} else {
set_current_state(TASK_RUNNING);
break;
}
down (&usblp->sem);
}
remove_wait_queue(&usblp->wait, &wait);
}
if (!usblp->dev) {
count = -ENODEV;
goto done;
}
if (usblp->readurb->status) {
err("usblp%d: error %d reading from printer",
usblp->minor, usblp->readurb->status);
usblp->readurb->dev = usblp->dev;
usblp->readcount = 0;
usb_submit_urb(usblp->readurb, GFP_KERNEL);
count = -EIO;
goto done;
}
count = count < usblp->readurb->actual_length - usblp->readcount ?
count : usblp->readurb->actual_length - usblp->readcount;
if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) {
count = -EFAULT;
goto done;
}
if ((usblp->readcount += count) == usblp->readurb->actual_length) {
usblp->readcount = 0;
usblp->readurb->dev = usblp->dev;
usblp->rcomplete = 0;
if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) {
count = -EIO;
goto done;
}
}
done:
up (&usblp->sem);
return count;
}
/*
* Checks for printers that have quirks, such as requiring unidirectional
* communication but reporting bidirectional; currently some HP printers
* have this flaw (HP 810, 880, 895, etc.), or needing an init string
* sent at each open (like some Epsons).
* Returns 1 if found, 0 if not found.
*
* HP recommended that we use the bidirectional interface but
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -