struct printer_dev {
spinlock_t lock; /* lock this structure */ /* lock buffer lists during read/write calls */ struct mutex lock_printer_io; struct usb_gadget *gadget;
s8 interface; struct usb_ep *in_ep, *out_ep; struct kref kref; struct list_head rx_reqs; /* List of free RX structs */ struct list_head rx_reqs_active; /* List of Active RX xfers */ struct list_head rx_buffers; /* List of completed xfers */ /* wait until there is data to be read. */
wait_queue_head_t rx_wait; struct list_head tx_reqs; /* List of free TX structs */ struct list_head tx_reqs_active; /* List of Active TX xfers */ /* Wait until there are write buffers available to use. */
wait_queue_head_t tx_wait; /* Wait until all write buffers have been sent. */
wait_queue_head_t tx_flush_wait; struct usb_request *current_rx_req;
size_t current_rx_bytes;
u8 *current_rx_buf;
u8 printer_status;
u8 reset_printer; int minor; struct cdev printer_cdev;
u8 printer_cdev_open;
wait_queue_head_t wait; unsigned q_len; char **pnp_string; /* We don't own memory! */ struct usb_function function;
};
switch (req->status) { default:
VDBG(dev, "tx err %d\n", req->status);
fallthrough; case -ECONNRESET: /* unlink */ case -ESHUTDOWN: /* disconnect etc */ break; case 0: break;
}
spin_lock(&dev->lock); /* Take the request struct off the active list and put it on the * free list.
*/
list_del_init(&req->list);
list_add(&req->list, &dev->tx_reqs);
wake_up_interruptible(&dev->tx_wait); if (likely(list_empty(&dev->tx_reqs_active)))
wake_up_interruptible(&dev->tx_flush_wait);
staticint
printer_open(struct inode *inode, struct file *fd)
{ struct printer_dev *dev; unsignedlong flags; int ret = -EBUSY;
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags); return -ENODEV;
}
if (!dev->printer_cdev_open) {
dev->printer_cdev_open = 1;
fd->private_data = dev;
ret = 0; /* Change the printer status to show that it's on-line. */
dev->printer_status |= PRINTER_SELECTED;
}
spin_lock_irqsave(&dev->lock, flags);
dev->printer_cdev_open = 0;
fd->private_data = NULL; /* Change printer status to show that the printer is off-line. */
dev->printer_status &= ~PRINTER_SELECTED;
spin_unlock_irqrestore(&dev->lock, flags);
kref_put(&dev->kref, printer_dev_free);
return 0;
}
/* This function must be called with interrupts turned off. */ staticvoid
setup_rx_reqs(struct printer_dev *dev)
{ struct usb_request *req;
while (likely(!list_empty(&dev->rx_reqs))) { int error;
/* The USB Host sends us whatever amount of data it wants to * so we always set the length field to the full USB_BUFSIZE. * If the amount of data is more than the read() caller asked * for it will be stored in the request buffer until it is * asked for by read().
*/
req->length = USB_BUFSIZE;
req->complete = rx_complete;
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock); if (error) {
DBG(dev, "rx submit --> %d\n", error);
list_add(&req->list, &dev->rx_reqs); break;
} /* if the req is empty, then add it into dev->rx_reqs_active. */ elseif (list_empty(&req->list))
list_add(&req->list, &dev->rx_reqs_active);
}
}
static ssize_t
printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{ struct printer_dev *dev = fd->private_data; unsignedlong flags;
size_t size;
size_t bytes_copied; struct usb_request *req; /* This is a pointer to the current USB rx request. */ struct usb_request *current_rx_req; /* This is the number of bytes in the current rx buffer. */
size_t current_rx_bytes; /* This is a pointer to the current rx buffer. */
u8 *current_rx_buf;
if (len == 0) return -EINVAL;
DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
/* Check if there is any data in the read buffers. Please note that * current_rx_bytes is the number of bytes in the current rx buffer. * If it is zero then check if there are any other rx_buffers that * are on the completed list. We are only out of data if all rx * buffers are empty.
*/ if ((current_rx_bytes == 0) &&
(likely(list_empty(&dev->rx_buffers)))) { /* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/* * If no data is available check if this is a NON-Blocking * call or not.
*/ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io); return -EAGAIN;
}
/* Sleep until data is available */
wait_event_interruptible(dev->rx_wait,
(likely(!list_empty(&dev->rx_buffers))));
spin_lock_irqsave(&dev->lock, flags); if (dev->interface < 0) goto out_disabled;
}
/* We have data to return then copy it to the caller's buffer.*/ while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
&& len) { if (current_rx_bytes == 0) {
req = container_of(dev->rx_buffers.next, struct usb_request, list);
list_del_init(&req->list);
/* We've disconnected or reset so return. */ if (dev->reset_printer) {
list_add(¤t_rx_req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io); return -EAGAIN;
}
if (dev->interface < 0) goto out_disabled;
/* If we not returning all the data left in this RX request * buffer then adjust the amount of data left in the buffer. * Othewise if we are done with this RX request buffer then * requeue it to get any incoming data from the USB host.
*/ if (size < current_rx_bytes) {
current_rx_bytes -= size;
current_rx_buf += size;
} else {
list_add(¤t_rx_req->list, &dev->rx_reqs);
current_rx_bytes = 0;
current_rx_buf = NULL;
current_rx_req = NULL;
}
}
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
/* Check if there is any available write buffers */ if (likely(list_empty(&dev->tx_reqs))) { /* Turn interrupts back on before sleeping. */
spin_unlock_irqrestore(&dev->lock, flags);
/* * If write buffers are available check if this is * a NON-Blocking call or not.
*/ if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
mutex_unlock(&dev->lock_printer_io); return -EAGAIN;
}
/* Sleep until a write buffer is available */
wait_event_interruptible(dev->tx_wait,
(likely(!list_empty(&dev->tx_reqs))));
spin_lock_irqsave(&dev->lock, flags); if (dev->interface < 0) goto out_disabled;
}
while (likely(!list_empty(&dev->tx_reqs)) && len) {
/* Check if we need to send a zero length packet. */ if (len > size) /* They will be more TX requests so no yet. */
req->zero = 0; else /* If the data amount is not a multiple of the * maxpacket size then send a zero length packet.
*/
req->zero = ((len % dev->in_ep->maxpacket) == 0);
/* Don't leave irqs off while doing memory copies */
spin_unlock_irqrestore(&dev->lock, flags);
/* We've disconnected or reset so free the req and buffer */ if (dev->reset_printer) {
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io); return -EAGAIN;
}
if (dev->interface < 0) goto out_disabled;
list_add(&req->list, &dev->tx_reqs_active);
/* here, we unlock, and only unlock, to avoid deadlock. */
spin_unlock(&dev->lock);
value = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
spin_lock(&dev->lock); if (value) {
list_move(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io); return -EAGAIN;
} if (dev->interface < 0) goto out_disabled;
}
if (!tx_list_empty) { /* Sleep until all data has been sent */
wait_event_interruptible(dev->tx_flush_wait,
(likely(list_empty(&dev->tx_reqs_active))));
}
inode_unlock(inode);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags); return -ENODEV;
}
switch (code) { case GADGET_GET_PRINTER_STATUS:
status = (int)dev->printer_status; break; case GADGET_SET_PRINTER_STATUS:
dev->printer_status = (u8)arg; break; default: /* could not handle ioctl */
DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
code);
status = -ENOTTY;
}
if (usb_ep_disable(dev->in_ep))
DBG(dev, "Failed to disable USB in_ep\n"); if (usb_ep_disable(dev->out_ep))
DBG(dev, "Failed to disable USB out_ep\n");
switch (ctrl->bRequestType&USB_TYPE_MASK) { case USB_TYPE_CLASS: switch (ctrl->bRequest) { case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */ /* Only one printer interface is supported. */ if ((wIndex>>8) != dev->interface) break;
if (!*dev->pnp_string) {
value = 0; break;
}
value = strlen(*dev->pnp_string);
buf[0] = (value >> 8) & 0xFF;
buf[1] = value & 0xFF;
memcpy(buf + 2, *dev->pnp_string, value);
DBG(dev, "1284 PNP String: %x %s\n", value,
*dev->pnp_string); break;
case GET_PORT_STATUS: /* Get Port Status */ /* Only one printer interface is supported. */ if (wIndex != dev->interface) break;
buf[0] = dev->printer_status;
value = min_t(u16, wLength, 1); break;
case SOFT_RESET: /* Soft Reset */ /* Only one printer interface is supported. */ if (wIndex != dev->interface) break;
/* all we really need is bulk IN/OUT */
in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); if (!in_ep) {
autoconf_fail:
dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
cdev->gadget->name); return -ENODEV;
}
out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); if (!out_ep) goto autoconf_fail;
/* assumes that all endpoints are dual-speed */
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ret = usb_assign_descriptors(f, fs_printer_function,
hs_printer_function, ss_printer_function,
ss_printer_function); if (ret) return ret;
dev->in_ep = in_ep;
dev->out_ep = out_ep;
ret = -ENOMEM; for (i = 0; i < dev->q_len; i++) {
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); if (!req) goto fail_tx_reqs;
list_add(&req->list, &dev->tx_reqs);
}
for (i = 0; i < dev->q_len; i++) {
req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); if (!req) goto fail_rx_reqs;
list_add(&req->list, &dev->rx_reqs);
}
/* Setup the sysfs files for the printer gadget. */
devt = MKDEV(major, dev->minor);
pdev = device_create(&usb_gadget_class, NULL, devt,
NULL, "g_printer%d", dev->minor); if (IS_ERR(pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
ret = PTR_ERR(pdev); goto fail_rx_reqs;
}
/* * Register a character device as an interface to a user mode * program that handles the printer specific functionality.
*/
cdev_init(&dev->printer_cdev, &printer_io_operations);
dev->printer_cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->printer_cdev, devt, 1); if (ret) {
ERROR(dev, "Failed to open char device\n"); goto fail_cdev_add;
}
/* Remove Character Device */
cdev_del(&dev->printer_cdev);
/* we must already have been disconnected ... no i/o may be active */
WARN_ON(!list_empty(&dev->tx_reqs_active));
WARN_ON(!list_empty(&dev->rx_reqs_active));
/* Free all memory for this driver. */ while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next, struct usb_request,
list);
list_del(&req->list);
printer_req_free(dev->in_ep, req);
}
if (dev->current_rx_req != NULL)
printer_req_free(dev->out_ep, dev->current_rx_req);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.