usblp.c

来自「linux 内核源代码」· C语言 代码 · 共 1,436 行 · 第 1/3 页

C
1,436
字号
	poll_wait(file, &usblp->rwait, wait);	poll_wait(file, &usblp->wwait, wait);	spin_lock_irqsave(&usblp->lock, flags);	ret = ((usblp->bidir && usblp->rcomplete) ? POLLIN  | POLLRDNORM : 0) |	   ((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0);	spin_unlock_irqrestore(&usblp->lock, flags);	return ret;}static long usblp_ioctl(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;	mutex_lock (&usblp->mut);	if (!usblp->present) {		retval = -ENODEV;		goto done;	}	if (usblp->sleeping) {		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 ((retval = usblp_read_status(usblp, usblp->statusbuf))) {					if (printk_ratelimit())						printk(KERN_ERR "usblp%d:"						    "failed reading printer status (%d)\n",						    usblp->minor, retval);					retval = -EIO;					goto done;				}				status = *usblp->statusbuf;				if (copy_to_user ((void __user *)arg, &status, sizeof(int)))					retval = -EFAULT;				break;			case LPABORT:				if (arg)					usblp->flags |= LP_ABORT;				else					usblp->flags &= ~LP_ABORT;				break;			default:				retval = -ENOTTY;		}done:	mutex_unlock (&usblp->mut);	return retval;}static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length){	struct urb *urb;	char *writebuf;	if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL)		return NULL;	if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) {		kfree(writebuf);		return NULL;	}	usb_fill_bulk_urb(urb, usblp->dev,		usb_sndbulkpipe(usblp->dev,		 usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),		writebuf, transfer_length, usblp_bulk_write, usblp);	urb->transfer_flags |= URB_FREE_BUFFER;	return urb;}static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos){	struct usblp *usblp = file->private_data;	struct urb *writeurb;	int rv;	int transfer_length;	ssize_t writecount = 0;	if (mutex_lock_interruptible(&usblp->wmut)) {		rv = -EINTR;		goto raise_biglock;	}	if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)		goto raise_wait;	while (writecount < count) {		/*		 * Step 1: Submit next block.		 */		if ((transfer_length = count - writecount) > USBLP_BUF_SIZE)			transfer_length = USBLP_BUF_SIZE;		rv = -ENOMEM;		if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL)			goto raise_urb;		usb_anchor_urb(writeurb, &usblp->urbs);		if (copy_from_user(writeurb->transfer_buffer,				   buffer + writecount, transfer_length)) {			rv = -EFAULT;			goto raise_badaddr;		}		spin_lock_irq(&usblp->lock);		usblp->wcomplete = 0;		spin_unlock_irq(&usblp->lock);		if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {			usblp->wstatus = 0;			spin_lock_irq(&usblp->lock);			usblp->no_paper = 0;			usblp->wcomplete = 1;			wake_up(&usblp->wwait);			spin_unlock_irq(&usblp->lock);			if (rv != -ENOMEM)				rv = -EIO;			goto raise_submit;		}		/*		 * Step 2: Wait for transfer to end, collect results.		 */		rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK));		if (rv < 0) {			if (rv == -EAGAIN) {				/* Presume that it's going to complete well. */				writecount += transfer_length;			}			if (rv == -ENOSPC) {				spin_lock_irq(&usblp->lock);				usblp->no_paper = 1;	/* Mark for poll(2) */				spin_unlock_irq(&usblp->lock);				writecount += transfer_length;			}			/* Leave URB dangling, to be cleaned on close. */			goto collect_error;		}		if (usblp->wstatus < 0) {			rv = -EIO;			goto collect_error;		}		/*		 * This is critical: it must be our URB, not other writer's.		 * The wmut exists mainly to cover us here.		 */		writecount += usblp->wstatus;	}	mutex_unlock(&usblp->wmut);	return writecount;raise_submit:raise_badaddr:	usb_unanchor_urb(writeurb);	usb_free_urb(writeurb);raise_urb:raise_wait:collect_error:		/* Out of raise sequence */	mutex_unlock(&usblp->wmut);raise_biglock:	return writecount ? writecount : rv;}/* * Notice that we fail to restart in a few cases: on EFAULT, on restart * error, etc. This is the historical behaviour. In all such cases we return * EIO, and applications loop in order to get the new read going. */static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, loff_t *ppos){	struct usblp *usblp = file->private_data;	ssize_t count;	ssize_t avail;	int rv;	if (!usblp->bidir)		return -EINVAL;	rv = usblp_rwait_and_lock(usblp, !!(file->f_flags & O_NONBLOCK));	if (rv < 0)		return rv;	if ((avail = usblp->rstatus) < 0) {		printk(KERN_ERR "usblp%d: error %d reading from printer\n",		    usblp->minor, (int)avail);		usblp_submit_read(usblp);		count = -EIO;		goto done;	}	count = len < avail - usblp->readcount ? len : avail - usblp->readcount;	if (count != 0 &&	    copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) {		count = -EFAULT;		goto done;	}	if ((usblp->readcount += count) == avail) {		if (usblp_submit_read(usblp) < 0) {			/* We don't want to leak USB return codes into errno. */			if (count == 0)				count = -EIO;			goto done;		}	}done:	mutex_unlock (&usblp->mut);	return count;}/* * Wait for the write path to come idle. * This is called under the ->wmut, so the idle path stays idle. * * Our write path has a peculiar property: it does not buffer like a tty, * but waits for the write to succeed. This allows our ->release to bug out * without waiting for writes to drain. But it obviously does not work * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use * select(2) or poll(2) to wait for the buffer to drain before closing. * Alternatively, set blocking mode with fcntl and issue a zero-size write. */static int usblp_wwait(struct usblp *usblp, int nonblock){	DECLARE_WAITQUEUE(waita, current);	int rc;	int err = 0;	add_wait_queue(&usblp->wwait, &waita);	for (;;) {		set_current_state(TASK_INTERRUPTIBLE);		if (mutex_lock_interruptible(&usblp->mut)) {			rc = -EINTR;			break;		}		rc = usblp_wtest(usblp, nonblock);		mutex_unlock(&usblp->mut);		if (rc <= 0)			break;		if (usblp->flags & LP_ABORT) {			if (schedule_timeout(msecs_to_jiffies(5000)) == 0) {				err = usblp_check_status(usblp, err);				if (err == 1) {	/* Paper out */					rc = -ENOSPC;					break;				}			}		} else {			schedule();		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&usblp->wwait, &waita);	return rc;}static int usblp_wtest(struct usblp *usblp, int nonblock){	unsigned long flags;	if (!usblp->present)		return -ENODEV;	if (signal_pending(current))		return -EINTR;	spin_lock_irqsave(&usblp->lock, flags);	if (usblp->wcomplete) {		spin_unlock_irqrestore(&usblp->lock, flags);		return 0;	}	spin_unlock_irqrestore(&usblp->lock, flags);	if (usblp->sleeping)		return -ENODEV;	if (nonblock)		return -EAGAIN;	return 1;}/* * Wait for read bytes to become available. This probably should have been * called usblp_r_lock_and_wait(), because we lock first. But it's a traditional * name for functions which lock and return. * * We do not use wait_event_interruptible because it makes locking iffy. */static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock){	DECLARE_WAITQUEUE(waita, current);	int rc;	add_wait_queue(&usblp->rwait, &waita);	for (;;) {		if (mutex_lock_interruptible(&usblp->mut)) {			rc = -EINTR;			break;		}		set_current_state(TASK_INTERRUPTIBLE);		if ((rc = usblp_rtest(usblp, nonblock)) < 0) {			mutex_unlock(&usblp->mut);			break;		}		if (rc == 0)	/* Keep it locked */			break;		mutex_unlock(&usblp->mut);		schedule();	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&usblp->rwait, &waita);	return rc;}static int usblp_rtest(struct usblp *usblp, int nonblock){	unsigned long flags;	if (!usblp->present)		return -ENODEV;	if (signal_pending(current))		return -EINTR;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?