diff options
Diffstat (limited to 'drivers/firewire/core-iso.c')
| -rw-r--r-- | drivers/firewire/core-iso.c | 139 | 
1 files changed, 89 insertions, 50 deletions
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index c003fa4e2db..38c0aa60b2c 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -29,6 +29,7 @@  #include <linux/slab.h>  #include <linux/spinlock.h>  #include <linux/vmalloc.h> +#include <linux/export.h>  #include <asm/byteorder.h> @@ -38,52 +39,73 @@   * Isochronous DMA context management   */ -int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, -		       int page_count, enum dma_data_direction direction) +int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)  { -	int i, j; -	dma_addr_t address; - -	buffer->page_count = page_count; -	buffer->direction = direction; +	int i; +	buffer->page_count = 0; +	buffer->page_count_mapped = 0;  	buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]),  				GFP_KERNEL);  	if (buffer->pages == NULL) -		goto out; +		return -ENOMEM; -	for (i = 0; i < buffer->page_count; i++) { +	for (i = 0; i < page_count; i++) {  		buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);  		if (buffer->pages[i] == NULL) -			goto out_pages; +			break; +	} +	buffer->page_count = i; +	if (i < page_count) { +		fw_iso_buffer_destroy(buffer, NULL); +		return -ENOMEM; +	} + +	return 0; +} + +int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card, +			  enum dma_data_direction direction) +{ +	dma_addr_t address; +	int i; +	buffer->direction = direction; + +	for (i = 0; i < buffer->page_count; i++) {  		address = dma_map_page(card->device, buffer->pages[i],  				       0, PAGE_SIZE, direction); -		if (dma_mapping_error(card->device, address)) { -			__free_page(buffer->pages[i]); -			goto out_pages; -		} +		if (dma_mapping_error(card->device, address)) +			break; +  		set_page_private(buffer->pages[i], address);  	} +	buffer->page_count_mapped = i; +	if (i < buffer->page_count) +		return -ENOMEM;  	return 0; +} - out_pages: -	for (j = 0; j < i; j++) { -		address = page_private(buffer->pages[j]); -		dma_unmap_page(card->device, address, -			       PAGE_SIZE, direction); -		__free_page(buffer->pages[j]); -	} -	kfree(buffer->pages); - out: -	buffer->pages = NULL; +int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, +		       int page_count, enum dma_data_direction direction) +{ +	int ret; + +	ret = fw_iso_buffer_alloc(buffer, page_count); +	if (ret < 0) +		return ret; + +	ret = fw_iso_buffer_map_dma(buffer, card, direction); +	if (ret < 0) +		fw_iso_buffer_destroy(buffer, card); -	return -ENOMEM; +	return ret;  }  EXPORT_SYMBOL(fw_iso_buffer_init); -int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) +int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer, +			  struct vm_area_struct *vma)  {  	unsigned long uaddr;  	int i, err; @@ -106,22 +128,25 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,  	int i;  	dma_addr_t address; -	for (i = 0; i < buffer->page_count; i++) { +	for (i = 0; i < buffer->page_count_mapped; i++) {  		address = page_private(buffer->pages[i]);  		dma_unmap_page(card->device, address,  			       PAGE_SIZE, buffer->direction); -		__free_page(buffer->pages[i]);  	} +	for (i = 0; i < buffer->page_count; i++) +		__free_page(buffer->pages[i]);  	kfree(buffer->pages);  	buffer->pages = NULL; +	buffer->page_count = 0; +	buffer->page_count_mapped = 0;  }  EXPORT_SYMBOL(fw_iso_buffer_destroy);  /* Convert DMA address to offset into virtually contiguous buffer. */  size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)  { -	int i; +	size_t i;  	dma_addr_t address;  	ssize_t offset; @@ -185,6 +210,18 @@ int fw_iso_context_queue(struct fw_iso_context *ctx,  }  EXPORT_SYMBOL(fw_iso_context_queue); +void fw_iso_context_queue_flush(struct fw_iso_context *ctx) +{ +	ctx->card->driver->flush_queue_iso(ctx); +} +EXPORT_SYMBOL(fw_iso_context_queue_flush); + +int fw_iso_context_flush_completions(struct fw_iso_context *ctx) +{ +	return ctx->card->driver->flush_iso_completions(ctx); +} +EXPORT_SYMBOL(fw_iso_context_flush_completions); +  int fw_iso_context_stop(struct fw_iso_context *ctx)  {  	return ctx->card->driver->stop_iso(ctx); @@ -196,9 +233,10 @@ EXPORT_SYMBOL(fw_iso_context_stop);   */  static int manage_bandwidth(struct fw_card *card, int irm_id, int generation, -			    int bandwidth, bool allocate, __be32 data[2]) +			    int bandwidth, bool allocate)  {  	int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0; +	__be32 data[2];  	/*  	 * On a 1394a IRM with low contention, try < 1 is enough. @@ -233,47 +271,48 @@ static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,  }  static int manage_channel(struct fw_card *card, int irm_id, int generation, -		u32 channels_mask, u64 offset, bool allocate, __be32 data[2]) +		u32 channels_mask, u64 offset, bool allocate)  { -	__be32 c, all, old; -	int i, ret = -EIO, retry = 5; +	__be32 bit, all, old; +	__be32 data[2]; +	int channel, ret = -EIO, retry = 5;  	old = all = allocate ? cpu_to_be32(~0) : 0; -	for (i = 0; i < 32; i++) { -		if (!(channels_mask & 1 << i)) +	for (channel = 0; channel < 32; channel++) { +		if (!(channels_mask & 1 << channel))  			continue;  		ret = -EBUSY; -		c = cpu_to_be32(1 << (31 - i)); -		if ((old & c) != (all & c)) +		bit = cpu_to_be32(1 << (31 - channel)); +		if ((old & bit) != (all & bit))  			continue;  		data[0] = old; -		data[1] = old ^ c; +		data[1] = old ^ bit;  		switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,  					   irm_id, generation, SCODE_100,  					   offset, data, 8)) {  		case RCODE_GENERATION:  			/* A generation change frees all channels. */ -			return allocate ? -EAGAIN : i; +			return allocate ? -EAGAIN : channel;  		case RCODE_COMPLETE:  			if (data[0] == old) -				return i; +				return channel;  			old = data[0];  			/* Is the IRM 1394a-2000 compliant? */ -			if ((data[0] & c) == (data[1] & c)) +			if ((data[0] & bit) == (data[1] & bit))  				continue;  			/* 1394-1995 IRM, fall through to retry. */  		default:  			if (retry) {  				retry--; -				i--; +				channel--;  			} else {  				ret = -EIO;  			} @@ -284,7 +323,7 @@ static int manage_channel(struct fw_card *card, int irm_id, int generation,  }  static void deallocate_channel(struct fw_card *card, int irm_id, -			       int generation, int channel, __be32 buffer[2]) +			       int generation, int channel)  {  	u32 mask;  	u64 offset; @@ -293,7 +332,7 @@ static void deallocate_channel(struct fw_card *card, int irm_id,  	offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :  				CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO; -	manage_channel(card, irm_id, generation, mask, offset, false, buffer); +	manage_channel(card, irm_id, generation, mask, offset, false);  }  /** @@ -322,7 +361,7 @@ static void deallocate_channel(struct fw_card *card, int irm_id,   */  void fw_iso_resource_manage(struct fw_card *card, int generation,  			    u64 channels_mask, int *channel, int *bandwidth, -			    bool allocate, __be32 buffer[2]) +			    bool allocate)  {  	u32 channels_hi = channels_mask;	/* channels 31...0 */  	u32 channels_lo = channels_mask >> 32;	/* channels 63...32 */ @@ -335,11 +374,11 @@ void fw_iso_resource_manage(struct fw_card *card, int generation,  	if (channels_hi)  		c = manage_channel(card, irm_id, generation, channels_hi,  				CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, -				allocate, buffer); +				allocate);  	if (channels_lo && c < 0) {  		c = manage_channel(card, irm_id, generation, channels_lo,  				CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, -				allocate, buffer); +				allocate);  		if (c >= 0)  			c += 32;  	} @@ -351,14 +390,14 @@ void fw_iso_resource_manage(struct fw_card *card, int generation,  	if (*bandwidth == 0)  		return; -	ret = manage_bandwidth(card, irm_id, generation, *bandwidth, -			       allocate, buffer); +	ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);  	if (ret < 0)  		*bandwidth = 0;  	if (allocate && ret < 0) {  		if (c >= 0) -			deallocate_channel(card, irm_id, generation, c, buffer); +			deallocate_channel(card, irm_id, generation, c);  		*channel = ret;  	}  } +EXPORT_SYMBOL(fw_iso_resource_manage);  | 
