X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fieee1394%2Fieee1394_transactions.c;h=40078ce930c86035ea2d5453ebc0f85851dd1fa1;hb=a2f72982e22b96862f8f15272732bd316d4db040;hp=a114b91d606db640ce5ffa46acc13abef2ebe6a2;hpb=fffcb480e4224f25c965b93fa65541bfc7dd732e;p=linux-2.6-omap-h63xx.git diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index a114b91d606..40078ce930c 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -9,19 +9,22 @@ * directory of the kernel sources for details. */ -#include #include -#include -#include - +#include +#include +#include +#include +#include /* because linux/wait.h is broken if CONFIG_SMP=n */ +#include + +#include #include +#include #include "ieee1394.h" #include "ieee1394_types.h" #include "hosts.h" #include "ieee1394_core.h" -#include "highlevel.h" -#include "nodemgr.h" #include "ieee1394_transactions.h" #define PREP_ASYNC_HEAD_ADDRESS(tc) \ @@ -31,6 +34,13 @@ packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \ packet->header[2] = addr & 0xffffffff +#ifndef HPSB_DEBUG_TLABELS +static +#endif +DEFINE_SPINLOCK(hpsb_tlabel_lock); + +static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); + static void fill_async_readquad(struct hpsb_packet *packet, u64 addr) { PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ); @@ -114,9 +124,41 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length, packet->tcode = TCODE_ISO_DATA; } +/* same as hpsb_get_tlabel, except that it returns immediately */ +static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet) +{ + unsigned long flags, *tp; + u8 *next; + int tlabel, n = NODEID_TO_NODE(packet->node_id); + + /* Broadcast transactions are complete once the request has been sent. + * Use the same transaction label for all broadcast transactions. */ + if (unlikely(n == ALL_NODES)) { + packet->tlabel = 0; + return 0; + } + tp = packet->host->tl_pool[n].map; + next = &packet->host->next_tl[n]; + + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + tlabel = find_next_zero_bit(tp, 64, *next); + if (tlabel > 63) + tlabel = find_first_zero_bit(tp, 64); + if (tlabel > 63) { + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + return -EAGAIN; + } + __set_bit(tlabel, tp); + *next = (tlabel + 1) & 63; + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); + + packet->tlabel = tlabel; + return 0; +} + /** * hpsb_get_tlabel - allocate a transaction label - * @packet: the packet who's tlabel/tpool we set + * @packet: the packet whose tlabel and tl_pool we set * * Every asynchronous transaction on the 1394 bus needs a transaction * label to match the response to the request. This label has to be @@ -130,42 +172,25 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length, * Return value: Zero on success, otherwise non-zero. A non-zero return * generally means there are no available tlabels. If this is called out * of interrupt or atomic context, then it will sleep until can return a - * tlabel. + * tlabel or a signal is received. */ int hpsb_get_tlabel(struct hpsb_packet *packet) { - unsigned long flags; - struct hpsb_tlabel_pool *tp; - int n = NODEID_TO_NODE(packet->node_id); - - if (unlikely(n == ALL_NODES)) - return 0; - tp = &packet->host->tpool[n]; - - if (irqs_disabled() || in_atomic()) { - if (down_trylock(&tp->count)) - return 1; - } else { - down(&tp->count); - } - - spin_lock_irqsave(&tp->lock, flags); - - packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next); - if (packet->tlabel > 63) - packet->tlabel = find_first_zero_bit(tp->pool, 64); - tp->next = (packet->tlabel + 1) % 64; - /* Should _never_ happen */ - BUG_ON(test_and_set_bit(packet->tlabel, tp->pool)); - tp->allocations++; - spin_unlock_irqrestore(&tp->lock, flags); - - return 0; + if (irqs_disabled() || in_atomic()) + return hpsb_get_tlabel_atomic(packet); + + /* NB: The macro wait_event_interruptible() is called with a condition + * argument with side effect. This is only possible because the side + * effect does not occur until the condition became true, and + * wait_event_interruptible() won't evaluate the condition again after + * that. */ + return wait_event_interruptible(tlabel_wq, + !hpsb_get_tlabel_atomic(packet)); } /** * hpsb_free_tlabel - free an allocated transaction label - * @packet: packet whos tlabel/tpool needs to be cleared + * @packet: packet whose tlabel and tl_pool needs to be cleared * * Frees the transaction label allocated with hpsb_get_tlabel(). The * tlabel has to be freed after the transaction is complete (i.e. response @@ -176,23 +201,31 @@ int hpsb_get_tlabel(struct hpsb_packet *packet) */ void hpsb_free_tlabel(struct hpsb_packet *packet) { - unsigned long flags; - struct hpsb_tlabel_pool *tp; - int n = NODEID_TO_NODE(packet->node_id); + unsigned long flags, *tp; + int tlabel, n = NODEID_TO_NODE(packet->node_id); if (unlikely(n == ALL_NODES)) return; - tp = &packet->host->tpool[n]; + tp = packet->host->tl_pool[n].map; + tlabel = packet->tlabel; + BUG_ON(tlabel > 63 || tlabel < 0); - BUG_ON(packet->tlabel > 63 || packet->tlabel < 0); + spin_lock_irqsave(&hpsb_tlabel_lock, flags); + BUG_ON(!__test_and_clear_bit(tlabel, tp)); + spin_unlock_irqrestore(&hpsb_tlabel_lock, flags); - spin_lock_irqsave(&tp->lock, flags); - BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool)); - spin_unlock_irqrestore(&tp->lock, flags); - - up(&tp->count); + wake_up_interruptible(&tlabel_wq); } +/** + * hpsb_packet_success - Make sense of the ack and reply codes + * + * Make sense of the ack and reply codes and return more convenient error codes: + * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can + * probably resolved by retry. -%EREMOTEIO = node suffers from an internal + * error. -%EACCES = this transaction is not allowed on requested address. + * -%EINVAL = invalid address at node. + */ int hpsb_packet_success(struct hpsb_packet *packet) { switch (packet->ack_code) { @@ -214,7 +247,7 @@ int hpsb_packet_success(struct hpsb_packet *packet) packet->node_id); return -EAGAIN; } - HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__); + BUG(); case ACK_BUSY_X: case ACK_BUSY_A: @@ -261,8 +294,7 @@ int hpsb_packet_success(struct hpsb_packet *packet) packet->ack_code, packet->node_id, packet->tcode); return -EAGAIN; } - - HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__); + BUG(); } struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node, @@ -346,6 +378,13 @@ struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer, } packet->host = host; + /* Because it is too difficult to determine all PHY speeds and link + * speeds here, we use S100... */ + packet->speed_code = IEEE1394_SPEED_100; + + /* ...and prevent hpsb_send_packet() from overriding it. */ + packet->node_id = LOCAL_BUS | ALL_NODES; + if (hpsb_get_tlabel(packet)) { hpsb_free_packet(packet); return NULL; @@ -475,6 +514,16 @@ struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, * avoid in kernel buffers for user space callers */ +/** + * hpsb_read - generic read function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet read request if @length == 4 and and a block read request otherwise. + * It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) { @@ -514,6 +563,16 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, return retval; } +/** + * hpsb_write - generic write function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet write request if @length == 4 and and a block write request + * otherwise. It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) {