usb转串口——u_serial.c_2
将数据送到tty层,由tty继续处理,数据在req的buffer里面。
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);/* leave data queued if tty was rx throttled */if (tty && tty_throttled(tty))break;switch (req->status) {case -ESHUTDOWN:disconnect = true;pr_vdebug("ttyGS%d: shutdown\n", port->port_num);break;default:/* presumably a transient fault */pr_warn("ttyGS%d: unexpected RX status %d\n",port->port_num, req->status);/* FALLTHROUGH */case 0:/* normal completion */break;}/* push data to (open) tty */if (req->actual && tty) {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(&port->port, packet,size);if (count)do_push = true;if (count != size) {/* stop pushing; TTY layer can't handle more */port->n_read += count;pr_vdebug("ttyGS%d: rx block %d/%d\n",port->port_num, count, req->actual);break;}port->n_read = 0;}list_move(&req->list, &port->read_pool);port->read_started--;}/* Push from tty to ldisc; this is handled by a workqueue,* so we won't get callbacks and can hold port_lock*/if (do_push)tty_flip_buffer_push(&port->port);/* 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 (!tty_throttled(tty)) {if (do_push)tasklet_schedule(&port->push);elsepr_warn("ttyGS%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);
static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{struct gs_port *port = ep->driver_data;/* Queue all received data until the tty layer is ready for it. */spin_lock(&port->port_lock);list_add_tail(&req->list, &port->read_queue);tasklet_schedule(&port->push);spin_unlock(&port->port_lock);
}
完成一次数据的读取,将数据送入req队列。
从链表中获取req,根据req的status,如果是0,则将req buf中的数据写入环形队列。
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{struct gs_port *port = ep->driver_data;spin_lock(&port->port_lock);list_add(&req->list, &port->write_pool);port->write_started--;switch (req->status) {default:/* presumably a transient fault */pr_warn("%s: unexpected %s status %d\n",__func__, ep->name, req->status);/* FALL THROUGH */case 0:/* normal completion */gs_start_tx(port);break;case -ESHUTDOWN:/* disconnect */pr_vdebug("%s: %s shutdown\n", __func__, ep->name);break;}spin_unlock(&port->port_lock);
}
req使用结束后将其释放所占内存,防止内核内存泄漏。
static void gs_free_requests(struct usb_ep *ep, struct list_head *head,int *allocated)
{struct usb_request *req;while (!list_empty(head)) {req = list_entry(head->next, struct usb_request, list);list_del(&req->list);gs_free_req(ep, req);if (allocated)(*allocated)--;}
}
创建一个req,分配内存挂接数据传输完成后的回调函数。
static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,void (*fn)(struct usb_ep *, struct usb_request *),int *allocated)
{int i;struct usb_request *req;int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't* do quite that many this time, don't fail ... we just won't* be as speedy as we might otherwise be.*/for (i = 0; i < n; i++) {req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);if (!req)return list_empty(head) ? -ENOMEM : 0;req->complete = fn;list_add_tail(&req->list, head);if (allocated)(*allocated)++;}return 0;
}
发起数据传送,目前在这里遇到过两次空指针操作,导致kernel crash,一次是 port->usb_port 为空,一次是port->port.tty为空,具体原因还在追查中。
static int gs_start_io(struct gs_port *port)
{struct list_head *head = &port->read_pool;struct usb_ep *ep = port->port_usb->out;int status;unsigned started;/* Allocate RX and TX I/O buffers. We can't easily do this much* earlier (with GFP_KERNEL) because the requests are coupled to* endpoints, as are the packet sizes we'll be using. Different* configurations may use different endpoints with a given port;* and high speed vs full speed changes packet sizes too.*/status = gs_alloc_requests(ep, head, gs_read_complete,&port->read_allocated);if (status)return status;status = gs_alloc_requests(port->port_usb->in, &port->write_pool,gs_write_complete, &port->write_allocated);if (status) {gs_free_requests(ep, head, &port->read_allocated);return status;}/* queue read requests */port->n_read = 0;started = gs_start_rx(port);/* unblock any pending writes into our circular buffer */if (started) {tty_wakeup(port->port.tty);} else {gs_free_requests(ep, head, &port->read_allocated);gs_free_requests(port->port_usb->in, &port->write_pool,&port->write_allocated);status = -EIO;}return status;
}
以下的函数是在tty层被调用的,判断串口的状态,如果没有打开,就开始数据传送。
static int gs_open(struct tty_struct *tty, struct file *file)
{int port_num = tty->index;struct gs_port *port;int status;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->port.count) {status = 0;port->port.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->port.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);status = 0;exit_unlock_port:spin_unlock_irq(&port->port_lock);return status;
}
返回数据的有效性和 port_usb 是否还存在。
static int gs_writes_finished(struct gs_port *p)
{int cond;/* return true on disconnect or empty buffer */spin_lock_irq(&p->port_lock);cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);spin_unlock_irq(&p->port_lock);return cond;
}
gs_close 关闭串口,关闭有几个条件:仅有程序在使用该设备,port->port.count 为1,调用port_usbd的disconnect函数断开连接后,释放所有申请的内存。port.tty置空,openclose标志置false。
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->port.count != 1) {if (port->port.count == 0)WARN_ON(1);else--port->port.count;goto exit;}pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", 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->port.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);elsegs_buf_clear(&port->port_write_buf);port->port.tty = NULL;port->openclose = false;pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",port->port_num, tty, file);wake_up(&port->close_wait);
exit:spin_unlock_irq(&port->port_lock);
}
将参数buf中的数据写入环形列表中,并启动传输,返回传输数据字节数。
static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{struct gs_port *port = tty->driver_data;unsigned long flags;pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",port->port_num, tty, count);spin_lock_irqsave(&port->port_lock, flags);if (count)count = gs_buf_put(&port->port_write_buf, buf, count);/* treat count == 0 as flush_chars() */if (port->port_usb)gs_start_tx(port);spin_unlock_irqrestore(&port->port_lock, flags);return count;
}
串形数据同行接口,将数据一个字节一个字节的写入环形buffer。
static int gs_put_char(struct tty_struct *tty, unsigned char ch)
{struct gs_port *port = tty->driver_data;unsigned long flags;int status;pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",port->port_num, tty, ch, __builtin_return_address(0));spin_lock_irqsave(&port->port_lock, flags);status = gs_buf_put(&port->port_write_buf, &ch, 1);spin_unlock_irqrestore(&port->port_lock, flags);return status;
}
刷新port中的数据。
static void gs_flush_chars(struct tty_struct *tty)
{struct gs_port *port = tty->driver_data;unsigned long flags;pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);spin_lock_irqsave(&port->port_lock, flags);if (port->port_usb)gs_start_tx(port);spin_unlock_irqrestore(&port->port_lock, flags);
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
