|
|
|
[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]
![]() |
![]() |