From patchwork Sat Nov 21 07:44:53 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "\(Exiting\) Baolin Wang" X-Patchwork-Id: 57097 Delivered-To: patch@linaro.org Received: by 10.112.155.196 with SMTP id vy4csp316482lbb; Fri, 20 Nov 2015 23:46:18 -0800 (PST) X-Received: by 10.67.22.99 with SMTP id hr3mr23890008pad.10.1448091978609; Fri, 20 Nov 2015 23:46:18 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s8si4160500pfa.140.2015.11.20.23.46.18; Fri, 20 Nov 2015 23:46:18 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dkim=neutral (body hash did not verify) header.i=@linaro-org.20150623.gappssmtp.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755150AbbKUHqI (ORCPT + 28 others); Sat, 21 Nov 2015 02:46:08 -0500 Received: from mail-pa0-f48.google.com ([209.85.220.48]:36271 "EHLO mail-pa0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751619AbbKUHpP (ORCPT ); Sat, 21 Nov 2015 02:45:15 -0500 Received: by pacdm15 with SMTP id dm15so139189482pac.3 for ; Fri, 20 Nov 2015 23:45:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id; bh=w8yH24R9JGG0A6mqBQA2w1yoo1KJ3B0+YTKDxX/HpA8=; b=nhSscfozRzMZNZpn0EqXtkVRgPMORMj7Hn+bSNTbHGgUvLsci9FlrWUyCSm7YCzcXs kG7FNUhNjn1gjCvvP/9O0ZUtdU7j4rJlg7kzi1hjVsKDADACIISCZVm2LyQFlu+Rz+lU HydRQ49zfnV0N77EuwqWmGYjxiMnFOs3onxehOB137jPeMeGZVPB7RodnSTudcJqXSn0 Hpn/8asS+VfBlD4J61ns9RKtiAcZjrxTCV7DYDxEbPyLeOSJIcEi19bj4GdMOopDYFS0 I50wlCLZVgAJ8svBY6SDcalDpRXBe6IpVwBJqqkubpZ/tzw11PVBQlWUwf1qBe2s7Q/l cVrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=w8yH24R9JGG0A6mqBQA2w1yoo1KJ3B0+YTKDxX/HpA8=; b=KmlYiF1TxxKNjxWlAwgRaUG6mKT0tDeyONA0775bCtwOnoj63n+wSjjEmPnfxrnPh/ fGgsUEBDmvpdq7lRhA+nLVRHlbKcK8U60c1+ikjfFmAWq3bG8bt+nADQIlT0BRIuA8bL tlhDylT0tfWkDJ0b3SbDUnisNKme3ROnv1Yw6NQLHsGM3gUyXwnUTlQ45F4wGWRVCcTq ASGBS5YyBAmj96GkR3R/pDd3UjlN8hzwpmoISsIJ/zxwowDzg8bv7BKWmsOBu+4dJFMk vR2UGvTedzwS+krMV8i5nYBpaC9yvMgiiGdtqtq7MRejcLXgiW1Q5iX9dgN2YhuA2vX9 6Thw== X-Gm-Message-State: ALoCoQlWkp23e9uw3k45vjJT5C0J4oFBd4c/n/9iAPbJ0oTQiRrA/ZTcn2nwV/zPLNjNh/s4GWIc X-Received: by 10.98.87.83 with SMTP id l80mr4056203pfb.126.1448091913390; Fri, 20 Nov 2015 23:45:13 -0800 (PST) Received: from baolinwangubtpc.spreadtrum.com ([175.111.195.49]) by smtp.gmail.com with ESMTPSA id mn6sm2386638pbc.83.2015.11.20.23.45.09 (version=TLS1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 20 Nov 2015 23:45:12 -0800 (PST) From: Baolin Wang To: balbi@ti.com, gregkh@linuxfoundation.org Cc: r.baldyga@samsung.com, fabio.estevam@freescale.com, Philip.Oberstaller@septentrio.com, peter@hurleysoftware.com, scottwood@freescale.com, broonie@kernel.org, baolin.wang@linaro.org, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2] usb: gadget: Add the console support for usb-to-serial port Date: Sat, 21 Nov 2015 15:44:53 +0800 Message-Id: X-Mailer: git-send-email 1.7.9.5 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org It dose not work when we want to use the usb-to-serial port based on one usb gadget as a console. Thus this patch adds the console initialization to support this request. To avoid the re-entrance when transferring data with usb endpoint, it introduces a kthread to do the IO transmission. Signed-off-by: Baolin Wang --- drivers/usb/gadget/Kconfig | 6 + drivers/usb/gadget/function/u_serial.c | 258 ++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+) -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 33834aa..be5aab9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -127,6 +127,12 @@ config USB_GADGET_STORAGE_NUM_BUFFERS a module parameter as well. If unsure, say 2. +config U_SERIAL_CONSOLE + bool "Serial gadget console support" + depends on USB_G_SERIAL + help + It supports the serial gadget can be used as a console. + source "drivers/usb/gadget/udc/Kconfig" # diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index f7771d8..6af145f 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "u_serial.h" @@ -79,6 +81,7 @@ */ #define QUEUE_SIZE 16 #define WRITE_BUF_SIZE 8192 /* TX only */ +#define GS_CONSOLE_BUF_SIZE 8192 /* circular buffer */ struct gs_buf { @@ -88,6 +91,17 @@ struct gs_buf { char *buf_put; }; +/* console info */ +struct gscons_info { + struct gs_port *port; + struct task_struct *console_thread; + struct gs_buf con_buf; + /* protect the buf and busy flag */ + spinlock_t con_lock; + int req_busy; + struct usb_request *console_req; +}; + /* * The port structure holds info for each port, one for each minor number * (and thus for each /dev/ node). @@ -1023,6 +1037,246 @@ static const struct tty_operations gs_tty_ops = { static struct tty_driver *gs_tty_driver; +#ifdef CONFIG_U_SERIAL_CONSOLE + +static struct gscons_info gscons_info; +static struct console gserial_cons; + +static struct usb_request *gs_request_new(struct usb_ep *ep) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!req) + return NULL; + + req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void gs_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (!req) + return; + + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct gscons_info *info = &gscons_info; + + switch (req->status) { + default: + pr_warn("%s: unexpected %s status %d\n", + __func__, ep->name, req->status); + case 0: + /* normal completion */ + spin_lock(&info->con_lock); + info->req_busy = 0; + spin_unlock(&info->con_lock); + + wake_up_process(info->console_thread); + break; + case -ESHUTDOWN: + /* disconnect */ + pr_vdebug("%s: %s shutdown\n", __func__, ep->name); + break; + } +} + +static int gs_console_connect(int port_num) +{ + struct gscons_info *info = &gscons_info; + struct gs_port *port; + struct usb_ep *ep; + + if (port_num != gserial_cons.index) { + pr_err("%s: port num [%d] is not support console\n", + __func__, port_num); + return -ENXIO; + } + + port = ports[port_num].port; + ep = port->port_usb->in; + if (!info->console_req) { + info->console_req = gs_request_new(ep); + if (!info->console_req) + return -ENOMEM; + info->console_req->complete = gs_complete_out; + } + + info->port = port; + spin_lock(&info->con_lock); + info->req_busy = 0; + spin_unlock(&info->con_lock); + pr_vdebug("port[%d] console connect!\n", port_num); + return 0; +} + +static void gs_console_disconnect(struct usb_ep *ep) +{ + struct gscons_info *info = &gscons_info; + struct usb_request *req = info->console_req; + + gs_request_free(req, ep); + info->console_req = NULL; +} + +static int gs_console_thread(void *data) +{ + struct gscons_info *info = &gscons_info; + struct gs_port *port; + struct usb_request *req; + struct usb_ep *ep; + int xfer, ret, count, size; + + do { + port = info->port; + set_current_state(TASK_INTERRUPTIBLE); + if (!port || !port->port_usb + || !port->port_usb->in || !info->console_req) + goto sched; + + req = info->console_req; + ep = port->port_usb->in; + + spin_lock_irq(&info->con_lock); + count = gs_buf_data_avail(&info->con_buf); + size = ep->maxpacket; + + if (count > 0 && !info->req_busy) { + set_current_state(TASK_RUNNING); + if (count < size) + size = count; + + xfer = gs_buf_get(&info->con_buf, req->buf, size); + req->length = xfer; + + spin_unlock(&info->con_lock); + ret = usb_ep_queue(ep, req, GFP_ATOMIC); + spin_lock(&info->con_lock); + if (ret < 0) + info->req_busy = 0; + else + info->req_busy = 1; + + spin_unlock_irq(&info->con_lock); + } else { + spin_unlock_irq(&info->con_lock); +sched: + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); + break; + } + schedule(); + } + } while (1); + + return 0; +} + +static int gs_console_setup(struct console *co, char *options) +{ + struct gscons_info *info = &gscons_info; + int status; + + info->port = NULL; + info->console_req = NULL; + info->req_busy = 0; + spin_lock_init(&info->con_lock); + + status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE); + if (status) { + pr_err("%s: allocate console buffer failed\n", __func__); + return status; + } + + info->console_thread = kthread_create(gs_console_thread, + co, "gs_console"); + if (IS_ERR(info->console_thread)) { + pr_err("%s: cannot create console thread\n", __func__); + gs_buf_free(&info->con_buf); + return PTR_ERR(info->console_thread); + } + wake_up_process(info->console_thread); + + return 0; +} + +static void gs_console_write(struct console *co, + const char *buf, unsigned count) +{ + struct gscons_info *info = &gscons_info; + unsigned long flags; + + spin_lock_irqsave(&info->con_lock, flags); + gs_buf_put(&info->con_buf, buf, count); + spin_unlock_irqrestore(&info->con_lock, flags); + + wake_up_process(info->console_thread); +} + +static struct tty_driver *gs_console_device(struct console *co, int *index) +{ + struct tty_driver **p = (struct tty_driver **)co->data; + + if (!*p) + return NULL; + + *index = co->index; + return *p; +} + +static struct console gserial_cons = { + .name = "ttyGS", + .write = gs_console_write, + .device = gs_console_device, + .setup = gs_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &gs_tty_driver, +}; + +static void gserial_console_init(void) +{ + register_console(&gserial_cons); +} + +static void gserial_console_exit(void) +{ + struct gscons_info *info = &gscons_info; + + unregister_console(&gserial_cons); + kthread_stop(info->console_thread); + gs_buf_free(&info->con_buf); +} + +#else + +static int gs_console_connect(int port_num) +{ + return 0; +} + +static void gs_console_disconnect(struct usb_ep *ep) +{ +} + +static void gserial_console_init(void) +{ +} + +static void gserial_console_exit(void) +{ +} + +#endif + static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { @@ -1096,6 +1350,7 @@ void gserial_free_line(unsigned char port_num) gserial_free_port(port); tty_unregister_device(gs_tty_driver, port_num); + gserial_console_exit(); } EXPORT_SYMBOL_GPL(gserial_free_line); @@ -1138,6 +1393,7 @@ int gserial_alloc_line(unsigned char *line_num) goto err; } *line_num = port_num; + gserial_console_init(); err: return ret; } @@ -1219,6 +1475,7 @@ int gserial_connect(struct gserial *gser, u8 port_num) gser->disconnect(gser); } + status = gs_console_connect(port_num); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -1277,6 +1534,7 @@ void gserial_disconnect(struct gserial *gser) port->read_allocated = port->read_started = port->write_allocated = port->write_started = 0; + gs_console_disconnect(gser->in); spin_unlock_irqrestore(&port->port_lock, flags); } EXPORT_SYMBOL_GPL(gserial_disconnect);