firewire: Implement proper transaction cancelation.
[linux-2.6.git] / drivers / firewire / fw-transaction.c
index 79563b2..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 header_get_extended_tcode(q)   (((q) >> 0) & 0xffff)
 
 #define phy_config_gap_count(gap_count)        (((gap_count) << 16) | (1 << 22))
-#define phy_config_root_id(node_id)    (((node_id) << 24) | (1 << 23))
+#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,23 +125,23 @@ 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;
        }
 }
 
 static void
-fw_fill_packet(struct fw_packet *packet, int tcode, int tlabel,
-              int node_id, int generation, int speed,
-              unsigned long long offset, void *payload, size_t length)
+fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+               int node_id, int generation, int speed,
+               unsigned long long offset, void *payload, size_t length)
 {
        int ext_tcode;
 
@@ -123,7 +155,7 @@ fw_fill_packet(struct fw_packet *packet, int tcode, int tlabel,
                header_retry(RETRY_X) |
                header_tlabel(tlabel) |
                header_tcode(tcode) |
-               header_destination(node_id | LOCAL_BUS);
+               header_destination(node_id);
        packet->header[1] =
                header_offset_high(offset >> 32) | header_source(0);
        packet->header[2] =
@@ -162,6 +194,7 @@ fw_fill_packet(struct fw_packet *packet, int tcode, int tlabel,
 
        packet->speed = speed;
        packet->generation = generation;
+       packet->ack = 0;
 }
 
 /**
@@ -190,7 +223,7 @@ fw_fill_packet(struct fw_packet *packet, int tcode, int tlabel,
  * @param tcode the tcode for this transaction.  Do not use
  *   TCODE_LOCK_REQUEST directly, insted use TCODE_LOCK_MASK_SWAP
  *   etc. to specify tcode and ext_tcode.
- * @param node_id the node_id of the destination node
+ * @param node_id the destination node ID (bus ID and PHY ID concatenated)
  * @param generation the generation for which node_id is valid
  * @param speed the speed to use for sending the request
  * @param offset the 48 bit offset on the destination node
@@ -240,8 +273,8 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t,
        t->callback = callback;
        t->callback_data = callback_data;
 
-       fw_fill_packet(&t->packet, tcode, t->tlabel,
-                      node_id, generation, speed, offset, payload, length);
+       fw_fill_request(&t->packet, tcode, t->tlabel,
+                       node_id, generation, speed, offset, payload, length);
        t->packet.callback = transmit_complete_callback;
 
        card->driver->send_request(card, &t->packet);
@@ -274,11 +307,15 @@ static void send_phy_packet(struct fw_card *card, u32 data, int generation)
        card->driver->send_request(card, packet);
 }
 
-void fw_send_force_root(struct fw_card *card, int node_id, int generation)
+void fw_send_phy_config(struct fw_card *card,
+                       int node_id, int generation, int gap_count)
 {
        u32 q;
 
-       q = phy_identifier(PHY_PACKET_CONFIG) | phy_config_root_id(node_id);
+       q = phy_identifier(PHY_PACKET_CONFIG) |
+               phy_config_root_id(node_id) |
+               phy_config_gap_count(gap_count);
+
        send_phy_packet(card, q, generation);
 }
 
@@ -294,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 *
@@ -332,15 +375,15 @@ static DEFINE_SPINLOCK(address_handler_lock);
 static LIST_HEAD(address_handler_list);
 
 const struct fw_address_region fw_low_memory_region =
-       { 0x000000000000ull, 0x000100000000ull };
+       { .start = 0x000000000000ULL, .end = 0x000100000000ULL,  };
 const struct fw_address_region fw_high_memory_region =
-       { 0x000100000000ull, 0xffffe0000000ull };
+       { .start = 0x000100000000ULL, .end = 0xffffe0000000ULL,  };
 const struct fw_address_region fw_private_region =
-       { 0xffffe0000000ull, 0xfffff0000000ull };
+       { .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL,  };
 const struct fw_address_region fw_csr_region =
-       { 0xfffff0000000ULL, 0xfffff0000800ull };
+       { .start = 0xfffff0000000ULL, .end = 0xfffff0000800ULL,  };
 const struct fw_address_region fw_unit_space_region =
-       { 0xfffff0000900ull, 0x1000000000000ull };
+       { .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
 EXPORT_SYMBOL(fw_low_memory_region);
 EXPORT_SYMBOL(fw_high_memory_region);
 EXPORT_SYMBOL(fw_private_region);
@@ -405,6 +448,7 @@ EXPORT_SYMBOL(fw_core_remove_address_handler);
 
 struct fw_request {
        struct fw_packet response;
+       u32 request_header[4];
        int ack;
        u32 length;
        u32 data[0];
@@ -420,23 +464,25 @@ free_response_callback(struct fw_packet *packet,
        kfree(request);
 }
 
-static void
-fw_fill_response(struct fw_packet *response,
-                u32 *request, u32 *data, size_t length)
+void
+fw_fill_response(struct fw_packet *response, u32 *request_header,
+                int rcode, void *payload, size_t length)
 {
        int tcode, tlabel, extended_tcode, source, destination;
 
-       tcode          = header_get_tcode(request[0]);
-       tlabel         = header_get_tlabel(request[0]);
-       source         = header_get_destination(request[0]);
-       destination    = header_get_source(request[1]);
-       extended_tcode = header_get_extended_tcode(request[3]);
+       tcode          = header_get_tcode(request_header[0]);
+       tlabel         = header_get_tlabel(request_header[0]);
+       source         = header_get_destination(request_header[0]);
+       destination    = header_get_source(request_header[1]);
+       extended_tcode = header_get_extended_tcode(request_header[3]);
 
        response->header[0] =
                header_retry(RETRY_1) |
                header_tlabel(tlabel) |
                header_destination(destination);
-       response->header[1] = header_source(source);
+       response->header[1] =
+               header_source(source) |
+               header_rcode(rcode);
        response->header[2] = 0;
 
        switch (tcode) {
@@ -450,7 +496,10 @@ fw_fill_response(struct fw_packet *response,
        case TCODE_READ_QUADLET_REQUEST:
                response->header[0] |=
                        header_tcode(TCODE_READ_QUADLET_RESPONSE);
-               response->header[3] = 0;
+               if (payload != NULL)
+                       response->header[3] = *(u32 *)payload;
+               else
+                       response->header[3] = 0;
                response->header_length = 16;
                response->payload_length = 0;
                break;
@@ -462,7 +511,7 @@ fw_fill_response(struct fw_packet *response,
                        header_data_length(length) |
                        header_extended_tcode(extended_tcode);
                response->header_length = 16;
-               response->payload = data;
+               response->payload = payload;
                response->payload_length = length;
                break;
 
@@ -471,26 +520,26 @@ fw_fill_response(struct fw_packet *response,
                return;
        }
 }
+EXPORT_SYMBOL(fw_fill_response);
 
 static struct fw_request *
-allocate_request(u32 *header, int ack,
-                int speed, int timestamp, int generation)
+allocate_request(struct fw_packet *p)
 {
        struct fw_request *request;
        u32 *data, length;
-       int request_tcode;
+       int request_tcode, t;
 
-       request_tcode = header_get_tcode(header[0]);
+       request_tcode = header_get_tcode(p->header[0]);
        switch (request_tcode) {
        case TCODE_WRITE_QUADLET_REQUEST:
-               data = &header[3];
+               data = &p->header[3];
                length = 4;
                break;
 
        case TCODE_WRITE_BLOCK_REQUEST:
        case TCODE_LOCK_REQUEST:
-               data = &header[4];
-               length = header_get_data_length(header[3]);
+               data = p->payload;
+               length = header_get_data_length(p->header[3]);
                break;
 
        case TCODE_READ_QUADLET_REQUEST:
@@ -500,7 +549,7 @@ allocate_request(u32 *header, int ack,
 
        case TCODE_READ_BLOCK_REQUEST:
                data = NULL;
-               length = header_get_data_length(header[3]);
+               length = header_get_data_length(p->header[3]);
                break;
 
        default:
@@ -512,16 +561,23 @@ allocate_request(u32 *header, int ack,
        if (request == NULL)
                return NULL;
 
-       request->response.speed = speed;
-       request->response.timestamp = timestamp;
-       request->response.generation = generation;
+       t = (p->timestamp & 0x1fff) + 4000;
+       if (t >= 8000)
+               t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000;
+       else
+               t = (p->timestamp & ~0x1fff) + t;
+
+       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 = ack;
+       request->ack = p->ack;
        request->length = length;
        if (data)
-               memcpy(request->data, data, length);
+               memcpy(request->data, p->payload, length);
 
-       fw_fill_response(&request->response, header, request->data, length);
+       memcpy(request->request_header, p->header, sizeof p->header);
 
        return request;
 }
@@ -529,52 +585,41 @@ allocate_request(u32 *header, int ack,
 void
 fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
 {
-       int response_tcode;
-
        /* Broadcast packets are reported as ACK_COMPLETE, so this
         * check is sufficient to ensure we don't send response to
         * broadcast packets or posted writes. */
        if (request->ack != ACK_PENDING)
                return;
 
-       request->response.header[1] |= header_rcode(rcode);
-       response_tcode = header_get_tcode(request->response.header[0]);
-       if (rcode != RCODE_COMPLETE)
-               /* Clear the data_length field. */
-               request->response.header[3] &= 0xffff;
-       else if (response_tcode == TCODE_READ_QUADLET_RESPONSE)
-               request->response.header[3] = request->data[0];
+       if (rcode == RCODE_COMPLETE)
+               fw_fill_response(&request->response, request->request_header,
+                                rcode, request->data, request->length);
+       else
+               fw_fill_response(&request->response, request->request_header,
+                                rcode, NULL, 0);
 
        card->driver->send_response(card, &request->response);
 }
 EXPORT_SYMBOL(fw_send_response);
 
 void
-fw_core_handle_request(struct fw_card *card,
-                      int speed, int ack, int timestamp,
-                      int generation, u32 length, u32 *header)
+fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
 {
        struct fw_address_handler *handler;
        struct fw_request *request;
        unsigned long long offset;
        unsigned long flags;
-       int tcode, destination, source, t;
+       int tcode, destination, source;
 
-       if (length > 2048) {
+       if (p->payload_length > 2048) {
                /* FIXME: send error response. */
                return;
        }
 
-       if (ack != ACK_PENDING && ack != ACK_COMPLETE)
+       if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
                return;
 
-       t = (timestamp & 0x1fff) + 4000;
-       if (t >= 8000)
-               t = (timestamp & ~0x1fff) + 0x2000 + t - 8000;
-       else
-               t = (timestamp & ~0x1fff) + t;
-
-       request = allocate_request(header, ack, speed, t, generation);
+       request = allocate_request(p);
        if (request == NULL) {
                /* FIXME: send statically allocated busy packet. */
                return;
@@ -582,10 +627,10 @@ fw_core_handle_request(struct fw_card *card,
 
        offset      =
                ((unsigned long long)
-                header_get_offset_high(header[1]) << 32) | header[2];
-       tcode       = header_get_tcode(header[0]);
-       destination = header_get_destination(header[0]);
-       source      = header_get_source(header[0]);
+                header_get_offset_high(p->header[1]) << 32) | p->header[2];
+       tcode       = header_get_tcode(p->header[0]);
+       destination = header_get_destination(p->header[0]);
+       source      = header_get_source(p->header[0]);
 
        spin_lock_irqsave(&address_handler_lock, flags);
        handler = lookup_enclosing_address_handler(&address_handler_list,
@@ -603,16 +648,14 @@ fw_core_handle_request(struct fw_card *card,
        else
                handler->address_callback(card, request,
                                          tcode, destination, source,
-                                         generation, speed, offset,
+                                         p->generation, p->speed, offset,
                                          request->data, request->length,
                                          handler->callback_data);
 }
 EXPORT_SYMBOL(fw_core_handle_request);
 
 void
-fw_core_handle_response(struct fw_card *card,
-                       int speed, int ack, int timestamp,
-                       u32 length, u32 *header)
+fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
 {
        struct fw_transaction *t;
        unsigned long flags;
@@ -620,11 +663,11 @@ fw_core_handle_response(struct fw_card *card,
        size_t data_length;
        int tcode, tlabel, destination, source, rcode;
 
-       tcode       = header_get_tcode(header[0]);
-       tlabel      = header_get_tlabel(header[0]);
-       destination = header_get_destination(header[0]);
-       source      = header_get_source(header[1]);
-       rcode       = header_get_rcode(header[1]);
+       tcode       = header_get_tcode(p->header[0]);
+       tlabel      = header_get_tlabel(p->header[0]);
+       destination = header_get_destination(p->header[0]);
+       source      = header_get_source(p->header[1]);
+       rcode       = header_get_rcode(p->header[1]);
 
        spin_lock_irqsave(&card->lock, flags);
        list_for_each_entry(t, &card->transaction_list, link) {
@@ -637,7 +680,8 @@ fw_core_handle_response(struct fw_card *card,
        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;
        }
 
@@ -646,7 +690,7 @@ fw_core_handle_response(struct fw_card *card,
 
        switch (tcode) {
        case TCODE_READ_QUADLET_RESPONSE:
-               data = (u32 *) &header[3];
+               data = (u32 *) &p->header[3];
                data_length = 4;
                break;
 
@@ -657,8 +701,8 @@ fw_core_handle_response(struct fw_card *card,
 
        case TCODE_READ_BLOCK_RESPONSE:
        case TCODE_LOCK_RESPONSE:
-               data = &header[4];
-               data_length = header_get_data_length(header[3]);
+               data = p->payload;
+               data_length = header_get_data_length(p->header[3]);
                break;
 
        default:
@@ -692,7 +736,7 @@ static const u32 vendor_textual_descriptor_data[] = {
 static struct fw_descriptor vendor_textual_descriptor = {
        .length = ARRAY_SIZE(vendor_textual_descriptor_data),
        .key = 0x81000000,
-       .data = vendor_textual_descriptor_data
+       .data = vendor_textual_descriptor_data,
 };
 
 static int __init fw_core_init(void)