firewire: Implement proper transaction cancelation.
[linux-2.6.git] / drivers / firewire / fw-transaction.c
index a116ffa..5394569 100644 (file)
@@ -48,7 +48,7 @@
 
 #define header_get_tcode(q)            (((q) >> 4) & 0x0f)
 #define header_get_tlabel(q)           (((q) >> 10) & 0x3f)
-#define header_get_rcode(q)            (((q) >> 4) & 0x0f)
+#define header_get_rcode(q)            (((q) >> 12) & 0x0f)
 #define header_get_destination(q)      (((q) >> 16) & 0xffff)
 #define header_get_source(q)           (((q) >> 16) & 0xffff)
 #define header_get_offset_high(q)      (((q) >> 0) & 0xffff)
 #define phy_config_root_id(node_id)    ((((node_id) & 0x3f) << 24) | (1 << 23))
 #define phy_identifier(id)             ((id) << 30)
 
-static void
-close_transaction(struct fw_transaction *t, struct fw_card *card, int rcode,
-                 u32 * payload, size_t length)
+static int
+close_transaction(struct fw_transaction *transaction,
+                 struct fw_card *card, int rcode,
+                 u32 *payload, size_t length)
 {
+       struct fw_transaction *t;
        unsigned long flags;
 
        spin_lock_irqsave(&card->lock, flags);
-       card->tlabel_mask &= ~(1 << t->tlabel);
-       list_del(&t->link);
+       list_for_each_entry(t, &card->transaction_list, link) {
+               if (t == transaction) {
+                       list_del(&t->link);
+                       card->tlabel_mask &= ~(1 << t->tlabel);
+                       break;
+               }
+       }
        spin_unlock_irqrestore(&card->lock, flags);
 
-       t->callback(card, rcode, payload, length, t->callback_data);
+       if (&t->link != &card->transaction_list) {
+               t->callback(card, rcode, payload, length, t->callback_data);
+               return 0;
+       }
+
+       return -ENOENT;
 }
 
+/* Only valid for transactions that are potentially pending (ie have
+ * been sent). */
+int
+fw_cancel_transaction(struct fw_card *card,
+                     struct fw_transaction *transaction)
+{
+       /* Cancel the packet transmission if it's still queued.  That
+        * will call the packet transmission callback which cancels
+        * the transaction. */
+
+       if (card->driver->cancel_packet(card, &transaction->packet) == 0)
+               return 0;
+
+       /* If the request packet has already been sent, we need to see
+        * if the transaction is still pending and remove it in that case. */
+
+       return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0);
+}
+EXPORT_SYMBOL(fw_cancel_transaction);
+
 static void
 transmit_complete_callback(struct fw_packet *packet,
                           struct fw_card *card, int status)
@@ -93,15 +125,15 @@ transmit_complete_callback(struct fw_packet *packet,
                close_transaction(t, card, RCODE_BUSY, NULL, 0);
                break;
        case ACK_DATA_ERROR:
+               close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0);
+               break;
        case ACK_TYPE_ERROR:
-               close_transaction(t, card, RCODE_SEND_ERROR, NULL, 0);
+               close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0);
                break;
        default:
-               /* FIXME: In this case, status is a negative errno,
-                * corresponding to an OHCI specific transmit error
-                * code.  We should map that to an RCODE instead of
-                * just the generic RCODE_SEND_ERROR. */
-               close_transaction(t, card, RCODE_SEND_ERROR, NULL, 0);
+               /* In this case the ack is really a juju specific
+                * rcode, so just forward that to the callback. */
+               close_transaction(t, card, status, NULL, 0);
                break;
        }
 }
@@ -162,6 +194,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
 
        packet->speed = speed;
        packet->generation = generation;
+       packet->ack = 0;
 }
 
 /**
@@ -298,8 +331,14 @@ void fw_flush_transactions(struct fw_card *card)
        card->tlabel_mask = 0;
        spin_unlock_irqrestore(&card->lock, flags);
 
-       list_for_each_entry_safe(t, next, &list, link)
+       list_for_each_entry_safe(t, next, &list, link) {
+               card->driver->cancel_packet(card, &t->packet);
+
+               /* At this point cancel_packet will never call the
+                * transaction callback, since we just took all the
+                * transactions out of the list.  So do it here.*/
                t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
+       }
 }
 
 static struct fw_address_handler *
@@ -425,7 +464,7 @@ free_response_callback(struct fw_packet *packet,
        kfree(request);
 }
 
-static void
+void
 fw_fill_response(struct fw_packet *response, u32 *request_header,
                 int rcode, void *payload, size_t length)
 {
@@ -457,7 +496,10 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
        case TCODE_READ_QUADLET_REQUEST:
                response->header[0] |=
                        header_tcode(TCODE_READ_QUADLET_RESPONSE);
-               response->header[3] = *(u32 *)payload;
+               if (payload != NULL)
+                       response->header[3] = *(u32 *)payload;
+               else
+                       response->header[3] = 0;
                response->header_length = 16;
                response->payload_length = 0;
                break;
@@ -478,6 +520,7 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
                return;
        }
 }
+EXPORT_SYMBOL(fw_fill_response);
 
 static struct fw_request *
 allocate_request(struct fw_packet *p)
@@ -527,11 +570,12 @@ allocate_request(struct fw_packet *p)
        request->response.speed = p->speed;
        request->response.timestamp = t;
        request->response.generation = p->generation;
+       request->response.ack = 0;
        request->response.callback = free_response_callback;
        request->ack = p->ack;
-       request->length = p->payload_length;
+       request->length = length;
        if (data)
-               memcpy(request->data, p->payload, p->payload_length);
+               memcpy(request->data, p->payload, length);
 
        memcpy(request->request_header, p->header, sizeof p->header);
 
@@ -636,7 +680,8 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
        spin_unlock_irqrestore(&card->lock, flags);
 
        if (&t->link == &card->transaction_list) {
-               fw_notify("Unsolicited response\n");
+               fw_notify("Unsolicited response (source %x, tlabel %x)\n",
+                         source, tlabel);
                return;
        }
 
@@ -656,7 +701,7 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
 
        case TCODE_READ_BLOCK_RESPONSE:
        case TCODE_LOCK_RESPONSE:
-               data = &p->header[4];
+               data = p->payload;
                data_length = header_get_data_length(p->header[3]);
                break;