📄 usblp.c
字号:
kfree (usblp);}static void usblp_unlink_urbs(struct usblp *usblp){ usb_kill_urb(usblp->writeurb); if (usblp->bidir) usb_kill_urb(usblp->readurb);}static int usblp_release(struct inode *inode, struct file *file){ struct usblp *usblp = file->private_data; down (&usblp_sem); usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); up (&usblp_sem); 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->rcomplete) ? 0 : POLLIN | POLLRDNORM) | (!usblp->wcomplete ? 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 newChannel; int status; int twoints[2]; int retval = 0; down (&usblp->sem); if (!usblp->present) { retval = -ENODEV; goto done; } dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) ); 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((void __user *) 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((void __user *)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 || le16_to_cpu(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((void __user *)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] = le16_to_cpu(usblp->dev->descriptor.idVendor); twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct); if (copy_to_user((void __user *)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; case IOCNR_SOFT_RESET: if (_IOC_DIR(cmd) != _IOC_NONE) { retval = -EINVAL; goto done; } retval = usblp_reset(usblp); break; default: retval = -ENOTTY; } else /* old-style ioctl value */ switch (cmd) { case LPGETSTATUS: if (usblp_read_status(usblp, usblp->statusbuf)) { if (printk_ratelimit()) err("usblp%d: failed reading printer status", usblp->minor); retval = -EIO; goto done; } status = *usblp->statusbuf; if (copy_to_user ((void __user *)arg, &status, sizeof(int))) retval = -EFAULT; break; default: retval = -ENOTTY; }done: up (&usblp->sem); return retval;}static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos){ DECLARE_WAITQUEUE(wait, current); struct usblp *usblp = file->private_data; int timeout, err = 0, transfer_length = 0; size_t writecount = 0; while (writecount < count) { if (!usblp->wcomplete) { barrier(); if (file->f_flags & O_NONBLOCK) { writecount += transfer_length; return writecount ? writecount : -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->present) { 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; } /* We must increment writecount here, and not at the * end of the loop. Otherwise, the final loop iteration may * be skipped, leading to incomplete printer output. */ writecount += transfer_length; if (writecount == count) { up(&usblp->sem); break; } transfer_length=(count - writecount); if (transfer_length > USBLP_BUF_SIZE) transfer_length = USBLP_BUF_SIZE; usblp->writeurb->transfer_buffer_length = transfer_length; if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount, transfer_length)) { up(&usblp->sem); return writecount ? writecount : -EFAULT; } usblp->writeurb->dev = usblp->dev; usblp->wcomplete = 0; err = usb_submit_urb(usblp->writeurb, GFP_KERNEL); if (err) { if (err != -ENOMEM) count = -EIO; else count = writecount ? writecount : -ENOMEM; up (&usblp->sem); break; } up (&usblp->sem); } return count;}static ssize_t usblp_read(struct file *file, char __user *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->present) { count = -ENODEV; goto done; } if (!usblp->rcomplete) { barrier(); if (file->f_flags & O_NONBLOCK) { count = -EAGAIN; goto done; } 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); down(&usblp->sem); break; } down (&usblp->sem); } remove_wait_queue(&usblp->wait, &wait); } if (!usblp->present) { 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; usblp->rcomplete = 0; if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) dbg("error submitting urb"); 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 + -