*/
#include <linux/completion.h>
+#include <linux/idr.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/uaccess.h>
-#include <asm/semaphore.h>
#include "fw-transaction.h"
#include "fw-topology.h"
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
+#define HEADER_DESTINATION_IS_BROADCAST(q) \
+ (((q) & HEADER_DESTINATION(0x3f)) == HEADER_DESTINATION(0x3f))
+
#define PHY_CONFIG_GAP_COUNT(gap_count) (((gap_count) << 16) | (1 << 22))
#define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
#define PHY_IDENTIFIER(id) ((id) << 30)
-static int
-close_transaction(struct fw_transaction *transaction,
- 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)
{
struct fw_transaction *t;
unsigned long flags;
spin_unlock_irqrestore(&card->lock, flags);
if (&t->link != &card->transaction_list) {
- t->callback(card, rcode, payload, length, t->callback_data);
+ t->callback(card, rcode, 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)
+int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction)
{
/*
* Cancel the packet transmission if it's still queued. That
* if the transaction is still pending and remove it in that case.
*/
- return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0);
+ return close_transaction(transaction, card, RCODE_CANCELLED);
}
EXPORT_SYMBOL(fw_cancel_transaction);
-static void
-transmit_complete_callback(struct fw_packet *packet,
- struct fw_card *card, int status)
+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);
switch (status) {
case ACK_COMPLETE:
- close_transaction(t, card, RCODE_COMPLETE, NULL, 0);
+ close_transaction(t, card, RCODE_COMPLETE);
break;
case ACK_PENDING:
t->timestamp = packet->timestamp;
case ACK_BUSY_X:
case ACK_BUSY_A:
case ACK_BUSY_B:
- close_transaction(t, card, RCODE_BUSY, NULL, 0);
+ close_transaction(t, card, RCODE_BUSY);
break;
case ACK_DATA_ERROR:
- close_transaction(t, card, RCODE_DATA_ERROR, NULL, 0);
+ close_transaction(t, card, RCODE_DATA_ERROR);
break;
case ACK_TYPE_ERROR:
- close_transaction(t, card, RCODE_TYPE_ERROR, NULL, 0);
+ close_transaction(t, card, RCODE_TYPE_ERROR);
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, NULL, 0);
+ close_transaction(t, card, status);
break;
}
}
-static void
-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
- int node_id, int source_id, int generation, int speed,
+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) {
+ packet->header[0] =
+ HEADER_DATA_LENGTH(length) |
+ destination_id |
+ HEADER_TCODE(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;
HEADER_RETRY(RETRY_X) |
HEADER_TLABEL(tlabel) |
HEADER_TCODE(tcode) |
- HEADER_DESTINATION(node_id);
+ HEADER_DESTINATION(destination_id);
packet->header[1] =
HEADER_OFFSET_HIGH(offset >> 32) | HEADER_SOURCE(source_id);
packet->header[2] =
packet->payload_length = 0;
break;
}
-
+ common:
packet->speed = speed;
packet->generation = generation;
packet->ack = 0;
+ packet->payload_bus = 0;
}
/**
* @param callback function to be called when the transaction is completed
* @param callback_data pointer to arbitrary data, which will be
* passed to the callback
+ *
+ * In case of asynchronous stream packets i.e. TCODE_STREAM_DATA, the caller
+ * needs to synthesize @destination_id with fw_stream_packet_destination_id().
*/
-void
-fw_send_request(struct fw_card *card, struct fw_transaction *t,
- int tcode, int node_id, int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length,
- fw_transaction_callback_t callback, void *callback_data)
+void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
+ int destination_id, int generation, int speed,
+ unsigned long long offset, void *payload, size_t length,
+ fw_transaction_callback_t callback, void *callback_data)
{
unsigned long flags;
- int tlabel, source;
+ int tlabel;
/*
* Bump the flush timer up 100ms first of all so we
spin_lock_irqsave(&card->lock, flags);
- source = card->node_id;
tlabel = card->current_tlabel;
if (card->tlabel_mask & (1 << tlabel)) {
spin_unlock_irqrestore(&card->lock, flags);
card->current_tlabel = (card->current_tlabel + 1) & 0x1f;
card->tlabel_mask |= (1 << tlabel);
- list_add_tail(&t->link, &card->transaction_list);
-
- spin_unlock_irqrestore(&card->lock, flags);
-
- /* Initialize rest of transaction, fill out packet and send it. */
- t->node_id = node_id;
+ t->node_id = destination_id;
t->tlabel = tlabel;
t->callback = callback;
t->callback_data = callback_data;
fw_fill_request(&t->packet, tcode, t->tlabel,
- node_id, source, generation,
+ destination_id, card->node_id, generation,
speed, offset, payload, length);
t->packet.callback = transmit_complete_callback;
+ list_add_tail(&t->link, &card->transaction_list);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
card->driver->send_request(card, &t->packet);
}
EXPORT_SYMBOL(fw_send_request);
-struct fw_phy_packet {
- struct fw_packet packet;
+struct transaction_callback_data {
struct completion done;
+ void *payload;
+ int rcode;
};
-static void
-transmit_phy_packet_callback(struct fw_packet *packet,
- struct fw_card *card, int status)
+static void transaction_callback(struct fw_card *card, int rcode,
+ void *payload, size_t length, void *data)
+{
+ struct transaction_callback_data *d = data;
+
+ if (rcode == RCODE_COMPLETE)
+ memcpy(d->payload, payload, length);
+ d->rcode = rcode;
+ complete(&d->done);
+}
+
+/**
+ * fw_run_transaction - send request and sleep until transaction is completed
+ *
+ * Returns the RCODE.
+ */
+int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+ int generation, int speed, unsigned long long offset,
+ void *payload, size_t length)
{
- struct fw_phy_packet *p =
- container_of(packet, struct fw_phy_packet, packet);
+ struct transaction_callback_data d;
+ struct fw_transaction t;
+
+ init_completion(&d.done);
+ d.payload = payload;
+ fw_send_request(card, &t, tcode, destination_id, generation, speed,
+ offset, payload, length, transaction_callback, &d);
+ wait_for_completion(&d.done);
- complete(&p->done);
+ return d.rcode;
}
+EXPORT_SYMBOL(fw_run_transaction);
+
+static DEFINE_MUTEX(phy_config_mutex);
+static DECLARE_COMPLETION(phy_config_done);
+
+static void transmit_phy_packet_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
+{
+ complete(&phy_config_done);
+}
+
+static struct fw_packet phy_config_packet = {
+ .header_length = 8,
+ .payload_length = 0,
+ .speed = SCODE_100,
+ .callback = transmit_phy_packet_callback,
+};
void fw_send_phy_config(struct fw_card *card,
int node_id, int generation, int gap_count)
{
- struct fw_phy_packet p;
+ long timeout = DIV_ROUND_UP(HZ, 10);
u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG) |
PHY_CONFIG_ROOT_ID(node_id) |
PHY_CONFIG_GAP_COUNT(gap_count);
- p.packet.header[0] = data;
- p.packet.header[1] = ~data;
- p.packet.header_length = 8;
- p.packet.payload_length = 0;
- p.packet.speed = SCODE_100;
- p.packet.generation = generation;
- p.packet.callback = transmit_phy_packet_callback;
- init_completion(&p.done);
-
- card->driver->send_request(card, &p.packet);
- wait_for_completion(&p.done);
+ mutex_lock(&phy_config_mutex);
+
+ phy_config_packet.header[0] = data;
+ phy_config_packet.header[1] = ~data;
+ phy_config_packet.generation = generation;
+ INIT_COMPLETION(phy_config_done);
+
+ card->driver->send_request(card, &phy_config_packet);
+ wait_for_completion_timeout(&phy_config_done, timeout);
+
+ mutex_unlock(&phy_config_mutex);
}
void fw_flush_transactions(struct fw_card *card)
}
}
-static struct fw_address_handler *
-lookup_overlapping_address_handler(struct list_head *list,
- unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_overlapping_address_handler(
+ struct list_head *list, unsigned long long offset, size_t length)
{
struct fw_address_handler *handler;
return NULL;
}
-static struct fw_address_handler *
-lookup_enclosing_address_handler(struct list_head *list,
- unsigned long long offset, size_t length)
+static struct fw_address_handler *lookup_enclosing_address_handler(
+ struct list_head *list, unsigned long long offset, size_t length)
{
struct fw_address_handler *handler;
static DEFINE_SPINLOCK(address_handler_lock);
static LIST_HEAD(address_handler_list);
-const struct fw_address_region fw_low_memory_region =
- { .start = 0x000000000000ULL, .end = 0x000100000000ULL, };
const struct fw_address_region fw_high_memory_region =
{ .start = 0x000100000000ULL, .end = 0xffffe0000000ULL, };
+EXPORT_SYMBOL(fw_high_memory_region);
+
+#if 0
+const struct fw_address_region fw_low_memory_region =
+ { .start = 0x000000000000ULL, .end = 0x000100000000ULL, };
const struct fw_address_region fw_private_region =
{ .start = 0xffffe0000000ULL, .end = 0xfffff0000000ULL, };
const struct fw_address_region fw_csr_region =
.end = CSR_REGISTER_BASE | CSR_CONFIG_ROM_END, };
const struct fw_address_region fw_unit_space_region =
{ .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
-EXPORT_SYMBOL(fw_low_memory_region);
-EXPORT_SYMBOL(fw_high_memory_region);
-EXPORT_SYMBOL(fw_private_region);
-EXPORT_SYMBOL(fw_csr_region);
-EXPORT_SYMBOL(fw_unit_space_region);
+#endif /* 0 */
/**
- * Allocate a range of addresses in the node space of the OHCI
- * controller. When a request is received that falls within the
- * specified address range, the specified callback is invoked. The
- * parameters passed to the callback give the details of the
- * particular request.
+ * fw_core_add_address_handler - register for incoming requests
+ * @handler: callback
+ * @region: region in the IEEE 1212 node space address range
+ *
+ * region->start, ->end, and handler->length have to be quadlet-aligned.
+ *
+ * When a request is received that falls within the specified address range,
+ * the specified callback is invoked. The parameters passed to the callback
+ * give the details of the particular request.
*
* Return value: 0 on success, non-zero otherwise.
* The start offset of the handler's address region is determined by
* fw_core_add_address_handler() and is returned in handler->offset.
- * The offset is quadlet-aligned.
*/
-int
-fw_core_add_address_handler(struct fw_address_handler *handler,
- const struct fw_address_region *region)
+int fw_core_add_address_handler(struct fw_address_handler *handler,
+ const struct fw_address_region *region)
{
struct fw_address_handler *other;
unsigned long flags;
int ret = -EBUSY;
+ if (region->start & 0xffff000000000003ULL ||
+ region->end & 0xffff000000000003ULL ||
+ region->start >= region->end ||
+ handler->length & 3 ||
+ handler->length == 0)
+ return -EINVAL;
+
spin_lock_irqsave(&address_handler_lock, flags);
- handler->offset = roundup(region->start, 4);
+ handler->offset = region->start;
while (handler->offset + handler->length <= region->end) {
other =
lookup_overlapping_address_handler(&address_handler_list,
handler->offset,
handler->length);
if (other != NULL) {
- handler->offset =
- roundup(other->offset + other->length, 4);
+ handler->offset += other->length;
} else {
list_add_tail(&handler->link, &address_handler_list);
ret = 0;
EXPORT_SYMBOL(fw_core_add_address_handler);
/**
- * Deallocate a range of addresses allocated with fw_allocate. This
- * will call the associated callback one last time with a the special
- * tcode TCODE_DEALLOCATE, to let the client destroy the registered
- * callback data. For convenience, the callback parameters offset and
- * length are set to the start and the length respectively for the
- * deallocated region, payload is set to NULL.
+ * fw_core_remove_address_handler - unregister an address handler
*/
void fw_core_remove_address_handler(struct fw_address_handler *handler)
{
u32 data[0];
};
-static void
-free_response_callback(struct fw_packet *packet,
- struct fw_card *card, int status)
+static void free_response_callback(struct fw_packet *packet,
+ struct fw_card *card, int status)
{
struct fw_request *request;
kfree(request);
}
-void
-fw_fill_response(struct fw_packet *response, u32 *request_header,
- int rcode, void *payload, 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;
BUG();
return;
}
+
+ response->payload_bus = 0;
}
EXPORT_SYMBOL(fw_fill_response);
-static struct fw_request *
-allocate_request(struct fw_packet *p)
+static struct fw_request *allocate_request(struct fw_packet *p)
{
struct fw_request *request;
u32 *data, length;
break;
default:
- BUG();
+ fw_error("ERROR - corrupt request received - %08x %08x %08x\n",
+ p->header[0], p->header[1], p->header[2]);
return NULL;
}
return request;
}
-void
-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
+void fw_send_response(struct fw_card *card,
+ struct fw_request *request, int rcode)
{
- /*
- * 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) {
+ /* unified transaction or broadcast transaction: don't respond */
+ if (request->ack != ACK_PENDING ||
+ HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
kfree(request);
return;
}
}
EXPORT_SYMBOL(fw_send_response);
-void
-fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
{
struct fw_address_handler *handler;
struct fw_request *request;
}
EXPORT_SYMBOL(fw_core_handle_request);
-void
-fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
{
struct fw_transaction *t;
unsigned long flags;
{ .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
.end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
-static void
-handle_topology_map(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void handle_topology_map(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ int speed, unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
int i, start, end;
__be32 *map;
{ .start = CSR_REGISTER_BASE,
.end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
-static void
-handle_registers(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, int speed,
- unsigned long long offset,
- void *payload, size_t length, void *callback_data)
+static void handle_registers(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ int speed, unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
{
int reg = offset & ~CSR_REGISTER_BASE;
unsigned long long bus_time;
__be32 *data = payload;
+ int rcode = RCODE_COMPLETE;
switch (reg) {
case CSR_CYCLE_TIME:
case CSR_BUS_TIME:
if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) {
- fw_send_response(card, request, RCODE_TYPE_ERROR);
+ rcode = RCODE_TYPE_ERROR;
break;
}
*data = cpu_to_be32(bus_time);
else
*data = cpu_to_be32(bus_time >> 25);
- fw_send_response(card, request, RCODE_COMPLETE);
+ break;
+
+ case CSR_BROADCAST_CHANNEL:
+ if (tcode == TCODE_READ_QUADLET_REQUEST)
+ *data = cpu_to_be32(card->broadcast_channel);
+ else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
+ card->broadcast_channel =
+ (be32_to_cpu(*data) & BROADCAST_CHANNEL_VALID) |
+ BROADCAST_CHANNEL_INITIAL;
+ else
+ rcode = RCODE_TYPE_ERROR;
break;
case CSR_BUS_MANAGER_ID:
case CSR_BUSY_TIMEOUT:
/* FIXME: Implement this. */
+
default:
- fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ rcode = RCODE_ADDRESS_ERROR;
break;
}
+
+ fw_send_response(card, request, rcode);
}
static struct fw_address_handler registers = {
static int __init fw_core_init(void)
{
- int retval;
+ int ret;
- retval = bus_register(&fw_bus_type);
- if (retval < 0)
- return retval;
+ ret = bus_register(&fw_bus_type);
+ if (ret < 0)
+ return ret;
fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
if (fw_cdev_major < 0) {
return fw_cdev_major;
}
- retval = fw_core_add_address_handler(&topology_map,
- &topology_map_region);
- BUG_ON(retval < 0);
-
- retval = fw_core_add_address_handler(®isters,
- ®isters_region);
- BUG_ON(retval < 0);
-
- /* Add the vendor textual descriptor. */
- retval = fw_core_add_descriptor(&vendor_id_descriptor);
- BUG_ON(retval < 0);
- retval = fw_core_add_descriptor(&model_id_descriptor);
- BUG_ON(retval < 0);
+ fw_core_add_address_handler(&topology_map, &topology_map_region);
+ fw_core_add_address_handler(®isters, ®isters_region);
+ fw_core_add_descriptor(&vendor_id_descriptor);
+ fw_core_add_descriptor(&model_id_descriptor);
return 0;
}
{
unregister_chrdev(fw_cdev_major, "firewire");
bus_unregister(&fw_bus_type);
+ idr_destroy(&fw_device_idr);
}
module_init(fw_core_init);