/** * usb_descriptor_fillbuf - fill buffer with descriptors * @buf: Buffer to be filled * @buflen: Size of buf * @src: Array of descriptor pointers, terminated by null pointer. * * Copies descriptors into the buffer, returning the length or a * negative error code if they can't all be copied. Useful when * assembling descriptors for an associated set of interfaces used * as part of configuring a composite device; or in other cases where * sets of descriptors need to be marshaled. */ int usb_descriptor_fillbuf(void *buf, unsigned buflen, const struct usb_descriptor_header **src) { u8 *dest = buf; struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *ep; if (!src) return -EINVAL; /* fill buffer from src[] until null descriptor ptr */ for (; NULL != *src; src++) { unsigned len = (*src)->bLength; if (len > buflen) return -EINVAL; memcpy(dest, *src, len); buflen -= len; dest += len; /* dump endpint descriptor to usb logger*/ switch (((*src)->bDescriptorType)) { case USB_DT_INTERFACE: interface = (struct usb_interface_descriptor *)*src; USB_LOGGER(INTERFACE_DESCRIPTOR, USB_DESCRIPTOR_FILLBUF, interface->bLength, \ interface->bDescriptorType, interface->bInterfaceNumber, \ interface->bAlternateSetting, interface->bNumEndpoints,\ interface->bInterfaceClass, interface->bInterfaceSubClass, \ interface->bInterfaceProtocol, interface->iInterface); break; case USB_DT_ENDPOINT: ep = (struct usb_endpoint_descriptor *)*src; USB_LOGGER(ENDPOINT_DESCRIPTOR, USB_DESCRIPTOR_FILLBUF, ep->bLength, \ ep->bDescriptorType, ep->bEndpointAddress, \ ep->bmAttributes, ep->wMaxPacketSize, ep->bInterval); break; default: USB_LOGGER(STRING, USB_DESCRIPTOR_FILLBUF, "type", (*src)->bDescriptorType); break; } } return dest - (u8 *)buf; }
static void gs_close(struct tty_struct *tty, struct file *file) { struct gs_port *port = tty->driver_data; struct gserial *gser; spin_lock_irq(&port->port_lock); if (port->open_count != 1) { if (port->open_count == 0) WARN_ON(1); else --port->open_count; goto exit; } pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); USB_LOGGER(GS_CLOSE,GS_CLOSE, port->port_num, tty, file); /* mark port as closing but in use; we can drop port lock * and sleep if necessary */ port->openclose = true; port->open_count = 0; gser = port->port_usb; if (gser && gser->disconnect) gser->disconnect(gser); /* wait for circular write buffer to drain, disconnect, or at * most GS_CLOSE_TIMEOUT seconds; then discard the rest */ if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->drain_wait, gs_writes_finished(port), GS_CLOSE_TIMEOUT * HZ); spin_lock_irq(&port->port_lock); gser = port->port_usb; } /* Iff we're disconnected, there can be no I/O in flight so it's * ok to free the circular buffer; else just scrub it. And don't * let the push tasklet fire again until we're re-opened. */ if (gser == NULL) gs_buf_free(&port->port_write_buf); else gs_buf_clear(&port->port_write_buf); tty->driver_data = NULL; port->port_tty = NULL; port->openclose = false; pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); wake_up_interruptible(&port->close_wait); exit: spin_unlock_irq(&port->port_lock); }
/* * gs_open sets up the link between a gs_port and its associated TTY. * That link is broken *only* by TTY close(), and all driver methods * know that. */ static int gs_open(struct tty_struct *tty, struct file *file) { int port_num = tty->index; struct gs_port *port; int status; if (port_num < 0 || port_num >= n_ports) return -ENXIO; do { mutex_lock(&ports[port_num].lock); port = ports[port_num].port; if (!port) status = -ENODEV; else { spin_lock_irq(&port->port_lock); /* already open? Great. */ if (port->open_count) { status = 0; port->open_count++; /* currently opening/closing? wait ... */ } else if (port->openclose) { status = -EBUSY; /* ... else we do the work */ } else { status = -EAGAIN; port->openclose = true; } spin_unlock_irq(&port->port_lock); } mutex_unlock(&ports[port_num].lock); switch (status) { default: /* fully handled */ return status; case -EAGAIN: /* must do the work */ break; case -EBUSY: /* wait for EAGAIN task to finish */ msleep(1); /* REVISIT could have a waitchannel here, if * concurrent open performance is important */ break; } } while (status != -EAGAIN); /* Do the "real open" */ spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ if (port->port_write_buf.buf_buf == NULL) { spin_unlock_irq(&port->port_lock); status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); spin_lock_irq(&port->port_lock); if (status) { pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", port->port_num, tty, file); port->openclose = false; goto exit_unlock_port; } } /* REVISIT if REMOVED (ports[].port NULL), abort the open * to let rmmod work faster (but this way isn't wrong). */ /* REVISIT maybe wait for "carrier detect" */ tty->driver_data = port; port->port_tty = tty; port->open_count = 1; port->openclose = false; /* if connected, start the I/O stream */ if (port->port_usb) { struct gserial *gser = port->port_usb; pr_debug("gs_open: start ttyGS%d\n", port->port_num); gs_start_io(port); if (gser->connect) gser->connect(gser); } pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); USB_LOGGER(GS_OPEN, GS_OPEN, port->port_num, tty, file); status = 0; exit_unlock_port: spin_unlock_irq(&port->port_lock); return status; }
/* * RX tasklet takes data out of the RX queue and hands it up to the TTY * layer until it refuses to take any more data (or is throttled back). * Then it issues reads for any further data. * * If the RX queue becomes full enough that no usb_request is queued, * the OUT endpoint may begin NAKing as soon as its FIFO fills up. * So QUEUE_SIZE packets plus however many the FIFO holds (usually two) * can be buffered before the TTY layer's buffers (currently 64 KB). */ static void gs_rx_push(unsigned long _port) { struct gs_port *port = (void *)_port; struct tty_struct *tty; struct list_head *queue = &port->read_queue; bool disconnect = false; bool do_push = false; /* hand any queued data to the tty */ spin_lock_irq(&port->port_lock); tty = port->port_tty; while (!list_empty(queue)) { struct usb_request *req; req = list_first_entry(queue, struct usb_request, list); /* discard data if tty was closed */ if (!tty) goto recycle; /* leave data queued if tty was rx throttled */ if (test_bit(TTY_THROTTLED, &tty->flags)) break; switch (req->status) { case -ESHUTDOWN: disconnect = true; pr_vdebug(PREFIX "%d: shutdown\n", port->port_num); break; default: /* presumably a transient fault */ pr_warning(PREFIX "%d: unexpected RX status %d\n", port->port_num, req->status); /* FALLTHROUGH */ case 0: /* normal completion */ break; } USB_LOGGER(GS_RX_PUSH, GS_RX_PUSH, port->port_num, req->actual, port->n_read); #define OUTPUT_BTYE_NUM 5 { int i,j = 0; char* prefix[] = {"p1","p2","p3","p4","p5"}; char* suffix[] = {"s1","s2","s3","s4","s5"}; for (i = 0; i < req->actual && i < OUTPUT_BTYE_NUM; i++) USB_LOGGER(HEX_NUM, GS_RX_PUSH, prefix[i], *((u8 *)req->buf+i)); if (req->actual >= OUTPUT_BTYE_NUM*2) { for(i = req->actual-1, j = 1; i >= (req->actual - OUTPUT_BTYE_NUM) \ && i >= OUTPUT_BTYE_NUM; i--,j++) { USB_LOGGER(HEX_NUM, GS_RX_PUSH, suffix[OUTPUT_BTYE_NUM-j], \ *((u8 *)req->buf+i)); } } } /* push data to (open) tty */ if (req->actual) { char *packet = req->buf; unsigned size = req->actual; unsigned n; int count; /* we may have pushed part of this packet already... */ n = port->n_read; if (n) { packet += n; size -= n; } count = tty_insert_flip_string(tty, packet, size); if (count) do_push = true; if (count != size) { /* stop pushing; TTY layer can't handle more */ port->n_read += count; pr_vdebug(PREFIX "%d: rx block %d/%d\n", port->port_num, count, req->actual); break; } port->n_read = 0; } recycle: list_move(&req->list, &port->read_pool); port->read_started--; } /* Push from tty to ldisc; without low_latency set this is handled by * a workqueue, so we won't get callbacks and can hold port_lock */ if (tty && do_push) { tty_flip_buffer_push(tty); } /* We want our data queue to become empty ASAP, keeping data * in the tty and ldisc (not here). If we couldn't push any * this time around, there may be trouble unless there's an * implicit tty_unthrottle() call on its way... * * REVISIT we should probably add a timer to keep the tasklet * from starving ... but it's not clear that case ever happens. */ if (!list_empty(queue) && tty) { if (!test_bit(TTY_THROTTLED, &tty->flags)) { if (do_push) tasklet_schedule(&port->push); else pr_warning(PREFIX "%d: RX not scheduled?\n", port->port_num); } } /* If we're still connected, refill the USB RX queue. */ if (!disconnect && port->port_usb) gs_start_rx(port); spin_unlock_irq(&port->port_lock); }
/* * gs_start_tx * * This function finds available write requests, calls * gs_send_packet to fill these packets with data, and * continues until either there are no more write requests * available or no more data to send. This function is * run whenever data arrives or write requests are available. * * Context: caller owns port_lock; port_usb is non-null. */ static int gs_start_tx(struct gs_port *port) /* __releases(&port->port_lock) __acquires(&port->port_lock) */ { struct list_head *pool = &port->write_pool; //struct usb_ep *in = port->port_usb->in; struct usb_ep *in; int status = 0; bool do_tty_wake = false; if (!port->port_usb) /* abort immediately after disconnect */ return -EINVAL; in = port->port_usb->in; while (!list_empty(pool)) { struct usb_request *req; int len; if (port->write_started >= QUEUE_SIZE) break; req = list_entry(pool->next, struct usb_request, list); len = gs_send_packet(port, req->buf, in->maxpacket); if (len == 0) { wake_up_interruptible(&port->drain_wait); break; } do_tty_wake = true; req->length = len; list_del(&req->list); req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", port->port_num, len, *((u8 *)req->buf), *((u8 *)req->buf+1), *((u8 *)req->buf+2)); USB_LOGGER(GS_START_TX, GS_START_TX, port->port_num, len); #define OUTPUT_BTYE_NUM 5 { int i,j = 0; char* prefix[] = {"p1","p2","p3","p4","p5"}; char* suffix[] = {"s1","s2","s3","s4","s5"}; for (i = 0; i < req->actual && i < OUTPUT_BTYE_NUM; i++) USB_LOGGER(HEX_NUM, GS_START_TX, prefix[i], *((u8 *)req->buf+i)); if (req->actual >= OUTPUT_BTYE_NUM*2) { for(i = req->actual-1, j = 1; i >= (req->actual - OUTPUT_BTYE_NUM) \ && i >= OUTPUT_BTYE_NUM; i--,j++) { USB_LOGGER(HEX_NUM, GS_START_TX, suffix[OUTPUT_BTYE_NUM-j], \ *((u8 *)req->buf+i)); } } } /* Drop lock while we call out of driver; completions * could be issued while we do so. Disconnection may * happen too; maybe immediately before we queue this! * * NOTE that we may keep sending data for a while after * the TTY closed (dev->ioport->port_tty is NULL). */ spin_unlock(&port->port_lock); status = usb_ep_queue(in, req, GFP_ATOMIC); spin_lock(&port->port_lock); if (status) { pr_debug("%s: %s %s err %d\n", __func__, "queue", in->name, status); list_add(&req->list, pool); break; } port->write_started++; /* abort immediately after disconnect */ if (!port->port_usb) break; } if (do_tty_wake && port->port_tty) tty_wakeup(port->port_tty); return status; }