diff options
author | Greg KH <greg@press.(none)> | 2005-06-28 01:07:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-28 01:07:56 -0400 |
commit | 8644d2a42bdba2d513f71c07eaf1b6f9b718b8eb (patch) | |
tree | c43b6c2fdf1b68b66906a2de69446dcec0f9af6b /drivers/usb/class/cdc-acm.c | |
parent | 1cde8a16815bd85c8137d1ea556398983c597c11 (diff) | |
parent | 99f95e5286df2f69edab8a04c7080d986ee4233b (diff) |
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 209 |
1 files changed, 175 insertions, 34 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 6d1f9b6aecff..69e859e0f51d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -106,6 +106,111 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int | |||
106 | acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) | 106 | acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) |
107 | 107 | ||
108 | /* | 108 | /* |
109 | * Write buffer management. | ||
110 | * All of these assume proper locks taken by the caller. | ||
111 | */ | ||
112 | |||
113 | static int acm_wb_alloc(struct acm *acm) | ||
114 | { | ||
115 | int i, wbn; | ||
116 | struct acm_wb *wb; | ||
117 | |||
118 | wbn = acm->write_current; | ||
119 | i = 0; | ||
120 | for (;;) { | ||
121 | wb = &acm->wb[wbn]; | ||
122 | if (!wb->use) { | ||
123 | wb->use = 1; | ||
124 | return wbn; | ||
125 | } | ||
126 | wbn = (wbn + 1) % ACM_NWB; | ||
127 | if (++i >= ACM_NWB) | ||
128 | return -1; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | static void acm_wb_free(struct acm *acm, int wbn) | ||
133 | { | ||
134 | acm->wb[wbn].use = 0; | ||
135 | } | ||
136 | |||
137 | static int acm_wb_is_avail(struct acm *acm) | ||
138 | { | ||
139 | int i, n; | ||
140 | |||
141 | n = 0; | ||
142 | for (i = 0; i < ACM_NWB; i++) { | ||
143 | if (!acm->wb[i].use) | ||
144 | n++; | ||
145 | } | ||
146 | return n; | ||
147 | } | ||
148 | |||
149 | static inline int acm_wb_is_used(struct acm *acm, int wbn) | ||
150 | { | ||
151 | return acm->wb[wbn].use; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Finish write. | ||
156 | */ | ||
157 | static void acm_write_done(struct acm *acm) | ||
158 | { | ||
159 | unsigned long flags; | ||
160 | int wbn; | ||
161 | |||
162 | spin_lock_irqsave(&acm->write_lock, flags); | ||
163 | acm->write_ready = 1; | ||
164 | wbn = acm->write_current; | ||
165 | acm_wb_free(acm, wbn); | ||
166 | acm->write_current = (wbn + 1) % ACM_NWB; | ||
167 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Poke write. | ||
172 | */ | ||
173 | static int acm_write_start(struct acm *acm) | ||
174 | { | ||
175 | unsigned long flags; | ||
176 | int wbn; | ||
177 | struct acm_wb *wb; | ||
178 | int rc; | ||
179 | |||
180 | spin_lock_irqsave(&acm->write_lock, flags); | ||
181 | if (!acm->dev) { | ||
182 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
183 | return -ENODEV; | ||
184 | } | ||
185 | |||
186 | if (!acm->write_ready) { | ||
187 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
188 | return 0; /* A white lie */ | ||
189 | } | ||
190 | |||
191 | wbn = acm->write_current; | ||
192 | if (!acm_wb_is_used(acm, wbn)) { | ||
193 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
194 | return 0; | ||
195 | } | ||
196 | wb = &acm->wb[wbn]; | ||
197 | |||
198 | acm->write_ready = 0; | ||
199 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
200 | |||
201 | acm->writeurb->transfer_buffer = wb->buf; | ||
202 | acm->writeurb->transfer_dma = wb->dmah; | ||
203 | acm->writeurb->transfer_buffer_length = wb->len; | ||
204 | acm->writeurb->dev = acm->dev; | ||
205 | |||
206 | if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) { | ||
207 | dbg("usb_submit_urb(write bulk) failed: %d", rc); | ||
208 | acm_write_done(acm); | ||
209 | } | ||
210 | return rc; | ||
211 | } | ||
212 | |||
213 | /* | ||
109 | * Interrupt handlers for various ACM device responses | 214 | * Interrupt handlers for various ACM device responses |
110 | */ | 215 | */ |
111 | 216 | ||
@@ -237,17 +342,13 @@ static void acm_rx_tasklet(unsigned long _acm) | |||
237 | static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) | 342 | static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) |
238 | { | 343 | { |
239 | struct acm *acm = (struct acm *)urb->context; | 344 | struct acm *acm = (struct acm *)urb->context; |
240 | dbg("Entering acm_write_bulk with status %d\n", urb->status); | ||
241 | |||
242 | if (!ACM_READY(acm)) | ||
243 | goto out; | ||
244 | 345 | ||
245 | if (urb->status) | 346 | dbg("Entering acm_write_bulk with status %d\n", urb->status); |
246 | dbg("nonzero write bulk status received: %d", urb->status); | ||
247 | 347 | ||
248 | schedule_work(&acm->work); | 348 | acm_write_done(acm); |
249 | out: | 349 | acm_write_start(acm); |
250 | acm->ready_for_write = 1; | 350 | if (ACM_READY(acm)) |
351 | schedule_work(&acm->work); | ||
251 | } | 352 | } |
252 | 353 | ||
253 | static void acm_softint(void *private) | 354 | static void acm_softint(void *private) |
@@ -351,32 +452,33 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c | |||
351 | { | 452 | { |
352 | struct acm *acm = tty->driver_data; | 453 | struct acm *acm = tty->driver_data; |
353 | int stat; | 454 | int stat; |
455 | unsigned long flags; | ||
456 | int wbn; | ||
457 | struct acm_wb *wb; | ||
458 | |||
354 | dbg("Entering acm_tty_write to write %d bytes,\n", count); | 459 | dbg("Entering acm_tty_write to write %d bytes,\n", count); |
355 | 460 | ||
356 | if (!ACM_READY(acm)) | 461 | if (!ACM_READY(acm)) |
357 | return -EINVAL; | 462 | return -EINVAL; |
358 | if (!acm->ready_for_write) | ||
359 | return 0; | ||
360 | if (!count) | 463 | if (!count) |
361 | return 0; | 464 | return 0; |
362 | 465 | ||
363 | count = (count > acm->writesize) ? acm->writesize : count; | 466 | spin_lock_irqsave(&acm->write_lock, flags); |
467 | if ((wbn = acm_wb_alloc(acm)) < 0) { | ||
468 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
469 | acm_write_start(acm); | ||
470 | return 0; | ||
471 | } | ||
472 | wb = &acm->wb[wbn]; | ||
364 | 473 | ||
474 | count = (count > acm->writesize) ? acm->writesize : count; | ||
365 | dbg("Get %d bytes...", count); | 475 | dbg("Get %d bytes...", count); |
366 | memcpy(acm->write_buffer, buf, count); | 476 | memcpy(wb->buf, buf, count); |
367 | dbg(" Successfully copied.\n"); | 477 | wb->len = count; |
478 | spin_unlock_irqrestore(&acm->write_lock, flags); | ||
368 | 479 | ||
369 | acm->writeurb->transfer_buffer_length = count; | 480 | if ((stat = acm_write_start(acm)) < 0) |
370 | acm->writeurb->dev = acm->dev; | ||
371 | |||
372 | acm->ready_for_write = 0; | ||
373 | stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC); | ||
374 | if (stat < 0) { | ||
375 | dbg("usb_submit_urb(write bulk) failed"); | ||
376 | acm->ready_for_write = 1; | ||
377 | return stat; | 481 | return stat; |
378 | } | ||
379 | |||
380 | return count; | 482 | return count; |
381 | } | 483 | } |
382 | 484 | ||
@@ -385,7 +487,11 @@ static int acm_tty_write_room(struct tty_struct *tty) | |||
385 | struct acm *acm = tty->driver_data; | 487 | struct acm *acm = tty->driver_data; |
386 | if (!ACM_READY(acm)) | 488 | if (!ACM_READY(acm)) |
387 | return -EINVAL; | 489 | return -EINVAL; |
388 | return !acm->ready_for_write ? 0 : acm->writesize; | 490 | /* |
491 | * Do not let the line discipline to know that we have a reserve, | ||
492 | * or it might get too enthusiastic. | ||
493 | */ | ||
494 | return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; | ||
389 | } | 495 | } |
390 | 496 | ||
391 | static int acm_tty_chars_in_buffer(struct tty_struct *tty) | 497 | static int acm_tty_chars_in_buffer(struct tty_struct *tty) |
@@ -393,7 +499,10 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) | |||
393 | struct acm *acm = tty->driver_data; | 499 | struct acm *acm = tty->driver_data; |
394 | if (!ACM_READY(acm)) | 500 | if (!ACM_READY(acm)) |
395 | return -EINVAL; | 501 | return -EINVAL; |
396 | return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0; | 502 | /* |
503 | * This is inaccurate (overcounts), but it works. | ||
504 | */ | ||
505 | return (ACM_NWB - acm_wb_is_avail(acm)) * acm->writesize; | ||
397 | } | 506 | } |
398 | 507 | ||
399 | static void acm_tty_throttle(struct tty_struct *tty) | 508 | static void acm_tty_throttle(struct tty_struct *tty) |
@@ -526,6 +635,39 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ | |||
526 | * USB probe and disconnect routines. | 635 | * USB probe and disconnect routines. |
527 | */ | 636 | */ |
528 | 637 | ||
638 | /* Little helper: write buffers free */ | ||
639 | static void acm_write_buffers_free(struct acm *acm) | ||
640 | { | ||
641 | int i; | ||
642 | struct acm_wb *wb; | ||
643 | |||
644 | for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) { | ||
645 | usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); | ||
646 | } | ||
647 | } | ||
648 | |||
649 | /* Little helper: write buffers allocate */ | ||
650 | static int acm_write_buffers_alloc(struct acm *acm) | ||
651 | { | ||
652 | int i; | ||
653 | struct acm_wb *wb; | ||
654 | |||
655 | for (wb = &acm->wb[0], i = 0; i < ACM_NWB; i++, wb++) { | ||
656 | wb->buf = usb_buffer_alloc(acm->dev, acm->writesize, GFP_KERNEL, | ||
657 | &wb->dmah); | ||
658 | if (!wb->buf) { | ||
659 | while (i != 0) { | ||
660 | --i; | ||
661 | --wb; | ||
662 | usb_buffer_free(acm->dev, acm->writesize, | ||
663 | wb->buf, wb->dmah); | ||
664 | } | ||
665 | return -ENOMEM; | ||
666 | } | ||
667 | } | ||
668 | return 0; | ||
669 | } | ||
670 | |||
529 | static int acm_probe (struct usb_interface *intf, | 671 | static int acm_probe (struct usb_interface *intf, |
530 | const struct usb_device_id *id) | 672 | const struct usb_device_id *id) |
531 | { | 673 | { |
@@ -700,7 +842,8 @@ skip_normal_probe: | |||
700 | acm->bh.data = (unsigned long) acm; | 842 | acm->bh.data = (unsigned long) acm; |
701 | INIT_WORK(&acm->work, acm_softint, acm); | 843 | INIT_WORK(&acm->work, acm_softint, acm); |
702 | spin_lock_init(&acm->throttle_lock); | 844 | spin_lock_init(&acm->throttle_lock); |
703 | acm->ready_for_write = 1; | 845 | spin_lock_init(&acm->write_lock); |
846 | acm->write_ready = 1; | ||
704 | 847 | ||
705 | buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); | 848 | buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); |
706 | if (!buf) { | 849 | if (!buf) { |
@@ -716,12 +859,10 @@ skip_normal_probe: | |||
716 | } | 859 | } |
717 | acm->read_buffer = buf; | 860 | acm->read_buffer = buf; |
718 | 861 | ||
719 | buf = usb_buffer_alloc(usb_dev, acm->writesize, GFP_KERNEL, &acm->write_dma); | 862 | if (acm_write_buffers_alloc(acm) < 0) { |
720 | if (!buf) { | ||
721 | dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); | 863 | dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); |
722 | goto alloc_fail4; | 864 | goto alloc_fail4; |
723 | } | 865 | } |
724 | acm->write_buffer = buf; | ||
725 | 866 | ||
726 | acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); | 867 | acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); |
727 | if (!acm->ctrlurb) { | 868 | if (!acm->ctrlurb) { |
@@ -750,9 +891,9 @@ skip_normal_probe: | |||
750 | acm->readurb->transfer_dma = acm->read_dma; | 891 | acm->readurb->transfer_dma = acm->read_dma; |
751 | 892 | ||
752 | usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), | 893 | usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), |
753 | acm->write_buffer, acm->writesize, acm_write_bulk, acm); | 894 | NULL, acm->writesize, acm_write_bulk, acm); |
754 | acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; | 895 | acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; |
755 | acm->writeurb->transfer_dma = acm->write_dma; | 896 | /* acm->writeurb->transfer_dma = 0; */ |
756 | 897 | ||
757 | dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); | 898 | dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); |
758 | 899 | ||
@@ -775,7 +916,7 @@ alloc_fail7: | |||
775 | alloc_fail6: | 916 | alloc_fail6: |
776 | usb_free_urb(acm->ctrlurb); | 917 | usb_free_urb(acm->ctrlurb); |
777 | alloc_fail5: | 918 | alloc_fail5: |
778 | usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); | 919 | acm_write_buffers_free(acm); |
779 | alloc_fail4: | 920 | alloc_fail4: |
780 | usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); | 921 | usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); |
781 | alloc_fail3: | 922 | alloc_fail3: |
@@ -806,7 +947,7 @@ static void acm_disconnect(struct usb_interface *intf) | |||
806 | 947 | ||
807 | flush_scheduled_work(); /* wait for acm_softint */ | 948 | flush_scheduled_work(); /* wait for acm_softint */ |
808 | 949 | ||
809 | usb_buffer_free(usb_dev, acm->writesize, acm->write_buffer, acm->write_dma); | 950 | acm_write_buffers_free(acm); |
810 | usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); | 951 | usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); |
811 | usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); | 952 | usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); |
812 | 953 | ||