packet->callback(packet, card, -ENODEV);
}
+static int
+dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+{
+ return -ENOENT;
+}
+
static int
dummy_enable_phys_dma(struct fw_card *card,
int node_id, int generation)
.update_phy_reg = dummy_update_phy_reg,
.set_config_rom = dummy_set_config_rom,
.send_request = dummy_send_request,
+ .cancel_packet = dummy_cancel_packet,
.send_response = dummy_send_response,
.enable_phys_dma = dummy_enable_phys_dma,
};
struct fw_ohci *ohci;
dma_addr_t descriptor_bus;
dma_addr_t buffer_bus;
+ struct fw_packet *current_packet;
struct list_head list;
ctx->descriptor_bus | z);
reg_write(ctx->ohci, control_set(ctx->regs),
CONTEXT_RUN | CONTEXT_WAKE);
+ ctx->current_packet = packet;
} else {
/* We dont return error codes from this function; all
* transmission errors are reported through the
at_context_stop(ctx);
+ /* If the head of the list isn't the packet that just got
+ * transmitted, the packet got cancelled before we finished
+ * transmitting it. */
+ if (ctx->current_packet != packet)
+ goto skip_to_next;
+
if (packet->payload_length > 0) {
dma_unmap_single(ohci->card.device, packet->payload_bus,
packet->payload_length, DMA_TO_DEVICE);
} else
complete_transmission(packet, evt - 16, &list);
+ skip_to_next:
/* If more packets are queued, set up the next one. */
if (!list_empty(&ctx->list))
at_context_setup_packet(ctx, &list);
at_context_transmit(&ohci->at_response_ctx, packet);
}
+static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+{
+ struct fw_ohci *ohci = fw_ohci(card);
+ LIST_HEAD(list);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (packet->ack == 0) {
+ fw_notify("cancelling packet %p (header[0]=%08x)\n",
+ packet, packet->header[0]);
+
+ complete_transmission(packet, RCODE_CANCELLED, &list);
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ do_packet_callbacks(ohci, &list);
+
+ /* Return success if we actually cancelled something. */
+ return list_empty(&list) ? -ENOENT : 0;
+}
+
static int
ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
{
.set_config_rom = ohci_set_config_rom,
.send_request = ohci_send_request,
.send_response = ohci_send_response,
+ .cancel_packet = ohci_cancel_packet,
.enable_phys_dma = ohci_enable_phys_dma,
.allocate_iso_context = ohci_allocate_iso_context,
spin_unlock_irqrestore(&device->card->lock, flags);
list_for_each_entry_safe(orb, next, &list, link) {
+ if (fw_cancel_transaction(device->card, &orb->t) == 0)
+ continue;
+
orb->rcode = RCODE_CANCELLED;
orb->callback(orb, NULL);
}
#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)
packet->speed = speed;
packet->generation = generation;
+ packet->ack = 0;
}
/**
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 *
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 = length;
void (*send_request) (struct fw_card *card, struct fw_packet *packet);
void (*send_response) (struct fw_card *card, struct fw_packet *packet);
+ /* Calling cancel is valid once a packet has been submitted. */
+ int (*cancel_packet) (struct fw_card *card, struct fw_packet *packet);
/* Allow the specified node ID to do direct DMA out and in of
* host memory. The card will disable this for all node when
void *data, size_t length,
fw_transaction_callback_t callback, void *callback_data);
+int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction);
+
void fw_flush_transactions(struct fw_card *card);
void fw_send_phy_config(struct fw_card *card,