+/*
+ * Write buffer management.
+ * All of these assume proper locks taken by the caller.
+ */
+
+static int acm_wb_alloc(struct acm *acm)
+{
+ int i, wbn;
+ struct acm_wb *wb;
+
+ wbn = acm->write_current;
+ i = 0;
+ for (;;) {
+ wb = &acm->wb[wbn];
+ if (!wb->use) {
+ wb->use = 1;
+ return wbn;
+ }
+ wbn = (wbn + 1) % ACM_NWB;
+ if (++i >= ACM_NWB)
+ return -1;
+ }
+}
+
+static void acm_wb_free(struct acm *acm, int wbn)
+{
+ acm->wb[wbn].use = 0;
+}
+
+static int acm_wb_is_avail(struct acm *acm)
+{
+ int i, n;
+
+ n = 0;
+ for (i = 0; i < ACM_NWB; i++) {
+ if (!acm->wb[i].use)
+ n++;
+ }
+ return n;
+}
+
+static inline int acm_wb_is_used(struct acm *acm, int wbn)
+{
+ return acm->wb[wbn].use;
+}
+
+/*
+ * Finish write.
+ */
+static void acm_write_done(struct acm *acm)
+{
+ unsigned long flags;
+ int wbn;
+
+ spin_lock_irqsave(&acm->write_lock, flags);
+ acm->write_ready = 1;
+ wbn = acm->write_current;
+ acm_wb_free(acm, wbn);
+ acm->write_current = (wbn + 1) % ACM_NWB;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+}
+
+/*
+ * Poke write.
+ */
+static int acm_write_start(struct acm *acm)
+{
+ unsigned long flags;
+ int wbn;
+ struct acm_wb *wb;
+ int rc;
+
+ spin_lock_irqsave(&acm->write_lock, flags);
+ if (!acm->dev) {
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return -ENODEV;
+ }
+
+ if (!acm->write_ready) {
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return 0; /* A white lie */
+ }
+
+ wbn = acm->write_current;
+ if (!acm_wb_is_used(acm, wbn)) {
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return 0;
+ }
+ wb = &acm->wb[wbn];
+
+ acm->write_ready = 0;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+
+ acm->writeurb->transfer_buffer = wb->buf;
+ acm->writeurb->transfer_dma = wb->dmah;
+ acm->writeurb->transfer_buffer_length = wb->len;
+ acm->writeurb->dev = acm->dev;
+
+ if ((rc = usb_submit_urb(acm->writeurb, GFP_ATOMIC)) < 0) {
+ dbg("usb_submit_urb(write bulk) failed: %d", rc);
+ acm_write_done(acm);
+ }
+ return rc;
+}
+