Google
  Web www.spinics.net

[PATCH] usb: serial: add cdc_notify callback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


added cdc notification support to struct
gserial. f_acm.c provides the first implementation.

Cc: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx>
---
 drivers/usb/gadget/f_acm.c    |   79 ++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/gadget/u_serial.c |   18 +++++++++
 drivers/usb/gadget/u_serial.h |    3 ++
 3 files changed, 99 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index d8faccf..f685419 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -66,6 +66,11 @@ static inline struct f_acm *func_to_acm(struct usb_function *f)
 	return container_of(f, struct f_acm, port.func);
 }
 
+static inline struct f_acm *port_to_acm(struct gserial *p)
+{
+	return container_of(p, struct f_acm, port);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* notification endpoint uses smallish and infrequent fixed-size messages */
@@ -117,7 +122,8 @@ static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
 	.bLength =		sizeof(acm_descriptor),
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
-	.bmCapabilities =	(1 << 1),
+	.bmCapabilities =	USB_CDC_CAP_LINE |
+				USB_CDC_CAP_BRK,
 };
 
 static struct usb_cdc_union_desc acm_union_desc __initdata = {
@@ -395,6 +401,73 @@ static void acm_disable(struct usb_function *f)
 	acm->notify->driver_data = NULL;
 }
 
+static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+/**
+ * acm_cdc_notify - CDC notification
+ *
+ * @port: gserial port to notify
+ * @type: notification type
+ * @value: Refer to cdc specs, wValue field.
+ * @data: data to be sent
+ * @length: size of data
+ *
+ * Returns zero on sucess or a negative errno.
+ */
+int acm_cdc_notify(struct gserial *port, u8 type, u16 value,
+		void *data, unsigned length)
+{
+	struct f_acm			*acm = port_to_acm(port);
+	struct usb_ep			*ep = acm->notify;
+	struct usb_request		*req;
+	struct usb_cdc_notification	*notify;
+	void				*buf;
+	int				status;
+
+	req = usb_ep_alloc_request(ep, GFP_KERNEL);
+	if (!req) {
+		status = -ENOMEM;
+		goto err_req;
+	}
+
+	req->buf = kmalloc(length, GFP_KERNEL);
+	if (!req->buf) {
+		status = -ENOMEM;
+		goto err_buf;
+	}
+
+	req->length = length;
+	req->complete = acm_cdc_notify_complete;
+	notify = req->buf;
+	buf = notify + 1;
+
+	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+		| USB_RECIP_INTERFACE;
+	notify->bNotificationType = type;
+	notify->wValue = __cpu_to_le16(value);
+	notify->wIndex = acm->ctrl_id;
+	notify->wLength = __cpu_to_le16(length);
+	memcpy(buf, data, length);
+
+	status = usb_ep_queue(ep, req, GFP_KERNEL);
+	if (status)
+		goto err_queue;
+
+	return 0;
+
+err_queue:
+	kfree(req->buf);
+err_buf:
+	usb_ep_free_request(ep, req);
+err_req:
+
+	return status;
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* ACM function driver setup/binding */
@@ -573,6 +646,9 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
 
 	acm->port_num = port_num;
 
+	/* cdc notification callback */
+	acm->port.notify = acm_cdc_notify;
+
 	acm->port.func.name = "acm";
 	acm->port.func.strings = acm_strings;
 	/* descriptors are per-instance copies */
@@ -587,3 +663,4 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
 		kfree(acm);
 	return status;
 }
+
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 88d4f54..c535978 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -922,6 +922,23 @@ static void gs_unthrottle(struct tty_struct *tty)
 			port->port_num, started);
 }
 
+static void gs_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct gs_port	*port = tty->driver_data;
+	int		status = 0;
+
+	pr_vdebug("gs_break_ctl: ttyGS%d, sending break\n",
+			port->port_num);
+
+	if (port->port_usb->notify)
+		status = port->port_usb->notify(port->port_usb,
+				USB_CDC_NOTIFY_SERIAL_STATE,
+				0, (u16 *) &break_state, sizeof(break_state));
+	if (status)
+		pr_vdebug("gs_break_ctrl: ttyGS%d failed to send break, "
+				"error %d\n", port->port_num, status);
+}
+
 static const struct tty_operations gs_tty_ops = {
 	.open =			gs_open,
 	.close =		gs_close,
@@ -931,6 +948,7 @@ static const struct tty_operations gs_tty_ops = {
 	.write_room =		gs_write_room,
 	.chars_in_buffer =	gs_chars_in_buffer,
 	.unthrottle =		gs_unthrottle,
+	.break_ctl =		gs_break_ctl,
 };
 
 /*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index 7912e9c..a01bb76 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -41,6 +41,9 @@ struct gserial {
 
 	/* REVISIT avoid this CDC-ACM support harder ... */
 	struct usb_cdc_line_coding port_line_coding;	/* 9600-8-N-1 etc */
+
+	/* cdc notification callback */
+	int (*notify)(struct gserial *p, u8 t, u16 v, void *d, unsigned l);
 };
 
 /* port setup/teardown is handled by gadget driver */
-- 
1.5.6.1.156.ge903b

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Home]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]    [Yosemite Photos]    [Video Projectors]     [PDAs]     [Free Online Dating]     [Hacking TiVo]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Devices]     [Big List of Linux Books]     [16.7MP]

Add to Google Powered by Linux