1/* 2 * Isochronous IO functionality 3 * 4 * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software Foundation, 18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 */ 20 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/dma-mapping.h> 24#include <linux/vmalloc.h> 25#include <linux/mm.h> 26 27#include "fw-transaction.h" 28#include "fw-topology.h" 29#include "fw-device.h" 30 31int 32fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, 33 int page_count, enum dma_data_direction direction) 34{ 35 int i, j, retval = -ENOMEM; 36 dma_addr_t address; 37 38 buffer->page_count = page_count; 39 buffer->direction = direction; 40 41 buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]), 42 GFP_KERNEL); 43 if (buffer->pages == NULL) 44 goto out; 45 46 for (i = 0; i < buffer->page_count; i++) { 47 buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO); 48 if (buffer->pages[i] == NULL) 49 goto out_pages; 50 51 address = dma_map_page(card->device, buffer->pages[i], 52 0, PAGE_SIZE, direction); 53 if (dma_mapping_error(address)) { 54 __free_page(buffer->pages[i]); 55 goto out_pages; 56 } 57 set_page_private(buffer->pages[i], address); 58 } 59 60 return 0; 61 62 out_pages: 63 for (j = 0; j < i; j++) { 64 address = page_private(buffer->pages[j]); 65 dma_unmap_page(card->device, address, 66 PAGE_SIZE, DMA_TO_DEVICE); 67 __free_page(buffer->pages[j]); 68 } 69 kfree(buffer->pages); 70 out: 71 buffer->pages = NULL; 72 return retval; 73} 74 75int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) 76{ 77 unsigned long uaddr; 78 int i, retval; 79 80 uaddr = vma->vm_start; 81 for (i = 0; i < buffer->page_count; i++) { 82 retval = vm_insert_page(vma, uaddr, buffer->pages[i]); 83 if (retval) 84 return retval; 85 uaddr += PAGE_SIZE; 86 } 87 88 return 0; 89} 90 91void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, 92 struct fw_card *card) 93{ 94 int i; 95 dma_addr_t address; 96 97 for (i = 0; i < buffer->page_count; i++) { 98 address = page_private(buffer->pages[i]); 99 dma_unmap_page(card->device, address, 100 PAGE_SIZE, DMA_TO_DEVICE); 101 __free_page(buffer->pages[i]); 102 } 103 104 kfree(buffer->pages); 105 buffer->pages = NULL; 106} 107 108struct fw_iso_context * 109fw_iso_context_create(struct fw_card *card, int type, 110 int channel, int speed, size_t header_size, 111 fw_iso_callback_t callback, void *callback_data) 112{ 113 struct fw_iso_context *ctx; 114 115 ctx = card->driver->allocate_iso_context(card, type, header_size); 116 if (IS_ERR(ctx)) 117 return ctx; 118 119 ctx->card = card; 120 ctx->type = type; 121 ctx->channel = channel; 122 ctx->speed = speed; 123 ctx->header_size = header_size; 124 ctx->callback = callback; 125 ctx->callback_data = callback_data; 126 127 return ctx; 128} 129EXPORT_SYMBOL(fw_iso_context_create); 130 131void fw_iso_context_destroy(struct fw_iso_context *ctx) 132{ 133 struct fw_card *card = ctx->card; 134 135 card->driver->free_iso_context(ctx); 136} 137EXPORT_SYMBOL(fw_iso_context_destroy); 138 139int 140fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags) 141{ 142 return ctx->card->driver->start_iso(ctx, cycle, sync, tags); 143} 144EXPORT_SYMBOL(fw_iso_context_start); 145 146int 147fw_iso_context_queue(struct fw_iso_context *ctx, 148 struct fw_iso_packet *packet, 149 struct fw_iso_buffer *buffer, 150 unsigned long payload) 151{ 152 struct fw_card *card = ctx->card; 153 154 return card->driver->queue_iso(ctx, packet, buffer, payload); 155} 156EXPORT_SYMBOL(fw_iso_context_queue); 157 158int 159fw_iso_context_stop(struct fw_iso_context *ctx) 160{ 161 return ctx->card->driver->stop_iso(ctx); 162} 163EXPORT_SYMBOL(fw_iso_context_stop); 164