diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2008-08-06 21:49:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-08-13 20:32:57 -0400 |
commit | 1f1ba11b64947051fc32aa15fcccef6463b433f7 (patch) | |
tree | 546d99300dc56f4fe744385263eb3864e843f83e /drivers/usb/gadget/u_serial.c | |
parent | 630c7aa80152881980e45c11584f22476f71959b (diff) |
usb gadget: issue notifications from ACM function
Update the CDC-ACM gadget code to support the peripheral-to-host
notifications when the tty is opened or closed, or issues a BREAK.
The serial framework code calls new generic hooks; right now only
CDC-ACM uses those hooks. This resolves several REVISIT comments
in the code. (Based on a patch from Felipe Balbi.)
Note that this doesn't expose USB_CDC_CAP_BRK to the host, since
this code still rejects USB_CDC_REQ_SEND_BREAK control requests
for host-to-peripheral BREAK signaling (received via /dev/ttyGS*).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Felipe Balbi <felipe.balbi@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/u_serial.c')
-rw-r--r-- | drivers/usb/gadget/u_serial.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 6641efa55639..53d59287f2bc 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c | |||
@@ -60,7 +60,8 @@ | |||
60 | * tty_struct links to the tty/filesystem framework | 60 | * tty_struct links to the tty/filesystem framework |
61 | * | 61 | * |
62 | * gserial <---> gs_port ... links will be null when the USB link is | 62 | * gserial <---> gs_port ... links will be null when the USB link is |
63 | * inactive; managed by gserial_{connect,disconnect}(). | 63 | * inactive; managed by gserial_{connect,disconnect}(). each gserial |
64 | * instance can wrap its own USB control protocol. | ||
64 | * gserial->ioport == usb_ep->driver_data ... gs_port | 65 | * gserial->ioport == usb_ep->driver_data ... gs_port |
65 | * gs_port->port_usb ... gserial | 66 | * gs_port->port_usb ... gserial |
66 | * | 67 | * |
@@ -181,7 +182,7 @@ static void gs_buf_clear(struct gs_buf *gb) | |||
181 | /* | 182 | /* |
182 | * gs_buf_data_avail | 183 | * gs_buf_data_avail |
183 | * | 184 | * |
184 | * Return the number of bytes of data available in the circular | 185 | * Return the number of bytes of data written into the circular |
185 | * buffer. | 186 | * buffer. |
186 | */ | 187 | */ |
187 | static unsigned gs_buf_data_avail(struct gs_buf *gb) | 188 | static unsigned gs_buf_data_avail(struct gs_buf *gb) |
@@ -282,7 +283,7 @@ gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) | |||
282 | * Allocate a usb_request and its buffer. Returns a pointer to the | 283 | * Allocate a usb_request and its buffer. Returns a pointer to the |
283 | * usb_request or NULL if there is an error. | 284 | * usb_request or NULL if there is an error. |
284 | */ | 285 | */ |
285 | static struct usb_request * | 286 | struct usb_request * |
286 | gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) | 287 | gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) |
287 | { | 288 | { |
288 | struct usb_request *req; | 289 | struct usb_request *req; |
@@ -306,7 +307,7 @@ gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) | |||
306 | * | 307 | * |
307 | * Free a usb_request and its buffer. | 308 | * Free a usb_request and its buffer. |
308 | */ | 309 | */ |
309 | static void gs_free_req(struct usb_ep *ep, struct usb_request *req) | 310 | void gs_free_req(struct usb_ep *ep, struct usb_request *req) |
310 | { | 311 | { |
311 | kfree(req->buf); | 312 | kfree(req->buf); |
312 | usb_ep_free_request(ep, req); | 313 | usb_ep_free_request(ep, req); |
@@ -788,10 +789,13 @@ static int gs_open(struct tty_struct *tty, struct file *file) | |||
788 | 789 | ||
789 | /* if connected, start the I/O stream */ | 790 | /* if connected, start the I/O stream */ |
790 | if (port->port_usb) { | 791 | if (port->port_usb) { |
792 | struct gserial *gser = port->port_usb; | ||
793 | |||
791 | pr_debug("gs_open: start ttyGS%d\n", port->port_num); | 794 | pr_debug("gs_open: start ttyGS%d\n", port->port_num); |
792 | gs_start_io(port); | 795 | gs_start_io(port); |
793 | 796 | ||
794 | /* REVISIT for ACM, issue "network connected" event */ | 797 | if (gser->connect) |
798 | gser->connect(gser); | ||
795 | } | 799 | } |
796 | 800 | ||
797 | pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); | 801 | pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); |
@@ -818,6 +822,7 @@ static int gs_writes_finished(struct gs_port *p) | |||
818 | static void gs_close(struct tty_struct *tty, struct file *file) | 822 | static void gs_close(struct tty_struct *tty, struct file *file) |
819 | { | 823 | { |
820 | struct gs_port *port = tty->driver_data; | 824 | struct gs_port *port = tty->driver_data; |
825 | struct gserial *gser; | ||
821 | 826 | ||
822 | spin_lock_irq(&port->port_lock); | 827 | spin_lock_irq(&port->port_lock); |
823 | 828 | ||
@@ -837,26 +842,27 @@ static void gs_close(struct tty_struct *tty, struct file *file) | |||
837 | port->openclose = true; | 842 | port->openclose = true; |
838 | port->open_count = 0; | 843 | port->open_count = 0; |
839 | 844 | ||
840 | if (port->port_usb) | 845 | gser = port->port_usb; |
841 | /* REVISIT for ACM, issue "network disconnected" event */; | 846 | if (gser && gser->disconnect) |
847 | gser->disconnect(gser); | ||
842 | 848 | ||
843 | /* wait for circular write buffer to drain, disconnect, or at | 849 | /* wait for circular write buffer to drain, disconnect, or at |
844 | * most GS_CLOSE_TIMEOUT seconds; then discard the rest | 850 | * most GS_CLOSE_TIMEOUT seconds; then discard the rest |
845 | */ | 851 | */ |
846 | if (gs_buf_data_avail(&port->port_write_buf) > 0 | 852 | if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { |
847 | && port->port_usb) { | ||
848 | spin_unlock_irq(&port->port_lock); | 853 | spin_unlock_irq(&port->port_lock); |
849 | wait_event_interruptible_timeout(port->drain_wait, | 854 | wait_event_interruptible_timeout(port->drain_wait, |
850 | gs_writes_finished(port), | 855 | gs_writes_finished(port), |
851 | GS_CLOSE_TIMEOUT * HZ); | 856 | GS_CLOSE_TIMEOUT * HZ); |
852 | spin_lock_irq(&port->port_lock); | 857 | spin_lock_irq(&port->port_lock); |
858 | gser = port->port_usb; | ||
853 | } | 859 | } |
854 | 860 | ||
855 | /* Iff we're disconnected, there can be no I/O in flight so it's | 861 | /* Iff we're disconnected, there can be no I/O in flight so it's |
856 | * ok to free the circular buffer; else just scrub it. And don't | 862 | * ok to free the circular buffer; else just scrub it. And don't |
857 | * let the push tasklet fire again until we're re-opened. | 863 | * let the push tasklet fire again until we're re-opened. |
858 | */ | 864 | */ |
859 | if (port->port_usb == NULL) | 865 | if (gser == NULL) |
860 | gs_buf_free(&port->port_write_buf); | 866 | gs_buf_free(&port->port_write_buf); |
861 | else | 867 | else |
862 | gs_buf_clear(&port->port_write_buf); | 868 | gs_buf_clear(&port->port_write_buf); |
@@ -974,6 +980,24 @@ static void gs_unthrottle(struct tty_struct *tty) | |||
974 | spin_unlock_irqrestore(&port->port_lock, flags); | 980 | spin_unlock_irqrestore(&port->port_lock, flags); |
975 | } | 981 | } |
976 | 982 | ||
983 | static int gs_break_ctl(struct tty_struct *tty, int duration) | ||
984 | { | ||
985 | struct gs_port *port = tty->driver_data; | ||
986 | int status = 0; | ||
987 | struct gserial *gser; | ||
988 | |||
989 | pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n", | ||
990 | port->port_num, duration); | ||
991 | |||
992 | spin_lock_irq(&port->port_lock); | ||
993 | gser = port->port_usb; | ||
994 | if (gser && gser->send_break) | ||
995 | status = gser->send_break(gser, duration); | ||
996 | spin_unlock_irq(&port->port_lock); | ||
997 | |||
998 | return status; | ||
999 | } | ||
1000 | |||
977 | static const struct tty_operations gs_tty_ops = { | 1001 | static const struct tty_operations gs_tty_ops = { |
978 | .open = gs_open, | 1002 | .open = gs_open, |
979 | .close = gs_close, | 1003 | .close = gs_close, |
@@ -983,6 +1007,7 @@ static const struct tty_operations gs_tty_ops = { | |||
983 | .write_room = gs_write_room, | 1007 | .write_room = gs_write_room, |
984 | .chars_in_buffer = gs_chars_in_buffer, | 1008 | .chars_in_buffer = gs_chars_in_buffer, |
985 | .unthrottle = gs_unthrottle, | 1009 | .unthrottle = gs_unthrottle, |
1010 | .break_ctl = gs_break_ctl, | ||
986 | }; | 1011 | }; |
987 | 1012 | ||
988 | /*-------------------------------------------------------------------------*/ | 1013 | /*-------------------------------------------------------------------------*/ |
@@ -1230,14 +1255,17 @@ int gserial_connect(struct gserial *gser, u8 port_num) | |||
1230 | 1255 | ||
1231 | /* REVISIT if waiting on "carrier detect", signal. */ | 1256 | /* REVISIT if waiting on "carrier detect", signal. */ |
1232 | 1257 | ||
1233 | /* REVISIT for ACM, issue "network connection" status notification: | 1258 | /* if it's already open, start I/O ... and notify the serial |
1234 | * connected if open_count, else disconnected. | 1259 | * protocol about open/close status (connect/disconnect). |
1235 | */ | 1260 | */ |
1236 | |||
1237 | /* if it's already open, start I/O */ | ||
1238 | if (port->open_count) { | 1261 | if (port->open_count) { |
1239 | pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); | 1262 | pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); |
1240 | gs_start_io(port); | 1263 | gs_start_io(port); |
1264 | if (gser->connect) | ||
1265 | gser->connect(gser); | ||
1266 | } else { | ||
1267 | if (gser->disconnect) | ||
1268 | gser->disconnect(gser); | ||
1241 | } | 1269 | } |
1242 | 1270 | ||
1243 | spin_unlock_irqrestore(&port->port_lock, flags); | 1271 | spin_unlock_irqrestore(&port->port_lock, flags); |