// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Core IEEE1394 transaction logic
*
* Copyright (C) 2004-2006 Kristian Hoegsberg <krh@bitplanet.net>
*/
#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <asm/byteorder.h>
#include "core.h"
#include "packet-header-definitions.h"
#include "phy-packet-definitions.h"
#include <trace/events/firewire.h>
#define HEADER_DESTINATION_IS_BROADCAST(header) \
((async_header_get_destination(header) & 0x3f) == 0x3f)
/* returns 0 if the split timeout handler is already running */
static int try_cancel_split_timeout(struct fw_transaction *t)
{
if (t->is_split_transaction)
return timer_delete(&t->split_timeout_timer);
else
return 1;
}
// card->transactions.lock must be acquired in advance.
static void remove_transaction_entry(struct fw_card *card, struct fw_transaction *entry)
{
list_del_init(&entry->link);
card->transactions.tlabel_mask &= ~(1ULL << entry->tlabel);
}
// Must be called without holding card->transactions.lock.
void fw_cancel_pending_transactions(struct fw_card *card)
{
struct fw_transaction *t, *tmp;
LIST_HEAD(pending_list);
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
list_for_each_entry_safe(t, tmp, &card->transactions.list, link) {
if (try_cancel_split_timeout(t))
list_move(&t->link, &pending_list);
}
}
list_for_each_entry_safe(t, tmp, &pending_list, link) {
list_del(&t->link);
if (!t->with_tstamp) {
t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0,
t->callback_data);
} else {
t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp, 0,
NULL, 0, t->callback_data);
}
}
}
// card->transactions.lock must be acquired in advance.
#define find_and_pop_transaction_entry(card, condition) \
({ \
struct fw_transaction *iter, *t = NULL; \
list_for_each_entry(iter, &card->transactions.list, link) { \
if (condition) { \
t = iter; \
break; \
} \
} \
if (t && try_cancel_split_timeout(t)) \
remove_transaction_entry(card, t); \
t; \
})
static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode,
u32 response_tstamp)
{
struct fw_transaction *t;
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
t = find_and_pop_transaction_entry(card, iter == transaction);
if (!t)
return -ENOENT;
}
if (!t->with_tstamp) {
t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data);
} else {
t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp, NULL, 0,
t->callback_data);
}
return 0;
}
/*
* Only valid for transactions that are potentially pending (ie have
* been sent).
*/
int fw_cancel_transaction(struct fw_card *card,
struct fw_transaction *transaction)
{
u32 tstamp;
/*
* 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.
*/
if (transaction->packet.ack == 0) {
// The timestamp is reused since it was just read now.
tstamp = transaction->packet.timestamp;
} else {
u32 curr_cycle_time = 0;
(void)fw_card_read_cycle_time(card, &curr_cycle_time);
tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time);
}
return close_transaction(transaction, card, RCODE_CANCELLED, tstamp);
}
EXPORT_SYMBOL(fw_cancel_transaction);
static void split_transaction_timeout_callback(struct timer_list *timer)
{
struct fw_transaction *t = timer_container_of(t, timer, split_timeout_timer);
struct fw_card *card = t->card;
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
if (list_empty(&t->link))
return;
remove_transaction_entry(card, t);
}
if (!t->with_tstamp) {
t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, t->callback_data);
} else {
t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp,
t->split_timeout_cycle, NULL, 0, t->callback_data);
}
}
// card->transactions.lock should be acquired in advance for the linked list.
static void start_split_transaction_timeout(struct fw_transaction *t, unsigned int delta)
{
if (list_empty(&t->link) || WARN_ON(t->is_split_transaction))
return;
t->is_split_transaction = true;
mod_timer(&t->split_timeout_timer, jiffies + delta);
}
static u32 compute_split_timeout_timestamp(struct fw_card *card, u32 request_timestamp);
static void transmit_complete_callback(struct fw_packet *packet,
struct fw_card *card, int status)
{
struct fw_transaction *t =
container_of(packet, struct fw_transaction, packet);
trace_async_request_outbound_complete((uintptr_t)t, card->index, packet->generation,
packet->speed, status, packet->timestamp);
switch (status) {
case ACK_COMPLETE:
close_transaction(t, card, RCODE_COMPLETE, packet->timestamp);
break;
case ACK_PENDING:
{
unsigned int delta;
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->split_timeout.lock) {
t->split_timeout_cycle =
compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff;
delta = card->split_timeout.jiffies;
}
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->transactions.lock)
start_split_transaction_timeout(t, delta);
break;
}
case ACK_BUSY_X:
case ACK_BUSY_A:
case ACK_BUSY_B:
close_transaction(t, card, RCODE_BUSY, packet->timestamp);
break;
case ACK_DATA_ERROR:
close_transaction(t, card, RCODE_DATA_ERROR, packet->timestamp);
break;
case ACK_TYPE_ERROR:
close_transaction(t, card, RCODE_TYPE_ERROR, packet->timestamp);
break;
default:
/*
* In this case the ack is really a juju specific
* rcode, so just forward that to the callback.
*/
close_transaction(t, card, status, packet->timestamp);
break;
}
}
static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
int destination_id, int source_id, int generation, int speed,
unsigned long long offset, void *payload, size_t length)
{
int ext_tcode;
if (tcode == TCODE_STREAM_DATA) {
// The value of destination_id argument should include tag, channel, and sy fields
// as isochronous packet header has.
packet->header[0] = destination_id;
isoc_header_set_data_length(packet->header, length);
isoc_header_set_tcode(packet->header, TCODE_STREAM_DATA);
packet->header_length = 4;
packet->payload = payload;
packet->payload_length = length;
goto common;
}
if (tcode > 0x10) {
ext_tcode = tcode & ~0x10;
tcode = TCODE_LOCK_REQUEST;
} else
ext_tcode = 0;
async_header_set_retry(packet->header, RETRY_X);
async_header_set_tlabel(packet->header, tlabel);
async_header_set_tcod
|