2007-05-08 04:33:32 +04:00
/*
* Isochronous IO functionality
2006-12-20 03:58:27 +03:00
*
* Copyright ( C ) 2006 Kristian Hoegsberg < krh @ bitplanet . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/dma-mapping.h>
# include <linux/vmalloc.h>
# include <linux/mm.h>
# include "fw-transaction.h"
# include "fw-topology.h"
2006-12-20 03:58:31 +03:00
# include "fw-device.h"
2006-12-20 03:58:27 +03:00
2007-02-17 01:34:38 +03:00
int
fw_iso_buffer_init ( struct fw_iso_buffer * buffer , struct fw_card * card ,
int page_count , enum dma_data_direction direction )
2006-12-20 03:58:27 +03:00
{
2007-02-17 01:34:38 +03:00
int i , j , retval = - ENOMEM ;
dma_addr_t address ;
buffer - > page_count = page_count ;
buffer - > direction = direction ;
buffer - > pages = kmalloc ( page_count * sizeof ( buffer - > pages [ 0 ] ) ,
GFP_KERNEL ) ;
if ( buffer - > pages = = NULL )
goto out ;
for ( i = 0 ; i < buffer - > page_count ; i + + ) {
2007-02-17 01:34:48 +03:00
buffer - > pages [ i ] = alloc_page ( GFP_KERNEL | GFP_DMA32 | __GFP_ZERO ) ;
2007-02-17 01:34:38 +03:00
if ( buffer - > pages [ i ] = = NULL )
goto out_pages ;
2007-03-04 16:45:18 +03:00
2007-02-17 01:34:38 +03:00
address = dma_map_page ( card - > device , buffer - > pages [ i ] ,
0 , PAGE_SIZE , direction ) ;
2008-07-26 06:44:49 +04:00
if ( dma_mapping_error ( card - > device , address ) ) {
2007-02-17 01:34:38 +03:00
__free_page ( buffer - > pages [ i ] ) ;
goto out_pages ;
}
set_page_private ( buffer - > pages [ i ] , address ) ;
2006-12-20 03:58:27 +03:00
}
return 0 ;
2007-02-06 22:49:40 +03:00
2007-02-17 01:34:38 +03:00
out_pages :
for ( j = 0 ; j < i ; j + + ) {
address = page_private ( buffer - > pages [ j ] ) ;
dma_unmap_page ( card - > device , address ,
2007-02-06 22:49:40 +03:00
PAGE_SIZE , DMA_TO_DEVICE ) ;
2007-02-17 01:34:38 +03:00
__free_page ( buffer - > pages [ j ] ) ;
}
kfree ( buffer - > pages ) ;
out :
buffer - > pages = NULL ;
return retval ;
}
int fw_iso_buffer_map ( struct fw_iso_buffer * buffer , struct vm_area_struct * vma )
{
unsigned long uaddr ;
int i , retval ;
uaddr = vma - > vm_start ;
for ( i = 0 ; i < buffer - > page_count ; i + + ) {
retval = vm_insert_page ( vma , uaddr , buffer - > pages [ i ] ) ;
if ( retval )
return retval ;
uaddr + = PAGE_SIZE ;
}
return 0 ;
2006-12-20 03:58:27 +03:00
}
2007-02-17 01:34:38 +03:00
void fw_iso_buffer_destroy ( struct fw_iso_buffer * buffer ,
struct fw_card * card )
2006-12-20 03:58:27 +03:00
{
int i ;
2007-02-17 01:34:38 +03:00
dma_addr_t address ;
2006-12-20 03:58:27 +03:00
2007-02-17 01:34:38 +03:00
for ( i = 0 ; i < buffer - > page_count ; i + + ) {
address = page_private ( buffer - > pages [ i ] ) ;
dma_unmap_page ( card - > device , address ,
2006-12-20 03:58:27 +03:00
PAGE_SIZE , DMA_TO_DEVICE ) ;
2007-02-17 01:34:38 +03:00
__free_page ( buffer - > pages [ i ] ) ;
}
2006-12-20 03:58:27 +03:00
2007-02-17 01:34:38 +03:00
kfree ( buffer - > pages ) ;
buffer - > pages = NULL ;
2006-12-20 03:58:27 +03:00
}
2007-02-17 01:34:40 +03:00
struct fw_iso_context *
2007-02-17 01:34:50 +03:00
fw_iso_context_create ( struct fw_card * card , int type ,
2007-03-15 00:34:54 +03:00
int channel , int speed , size_t header_size ,
2007-02-17 01:34:40 +03:00
fw_iso_callback_t callback , void * callback_data )
2006-12-20 03:58:27 +03:00
{
struct fw_iso_context * ctx ;
2007-03-15 00:34:54 +03:00
ctx = card - > driver - > allocate_iso_context ( card , type , header_size ) ;
2006-12-20 03:58:27 +03:00
if ( IS_ERR ( ctx ) )
return ctx ;
ctx - > card = card ;
ctx - > type = type ;
2007-02-17 01:34:50 +03:00
ctx - > channel = channel ;
ctx - > speed = speed ;
2007-02-17 01:34:40 +03:00
ctx - > header_size = header_size ;
2006-12-20 03:58:27 +03:00
ctx - > callback = callback ;
ctx - > callback_data = callback_data ;
return ctx ;
}
void fw_iso_context_destroy ( struct fw_iso_context * ctx )
{
struct fw_card * card = ctx - > card ;
card - > driver - > free_iso_context ( ctx ) ;
}
int
2007-03-15 00:34:54 +03:00
fw_iso_context_start ( struct fw_iso_context * ctx , int cycle , int sync , int tags )
2006-12-20 03:58:27 +03:00
{
2007-03-15 00:34:54 +03:00
return ctx - > card - > driver - > start_iso ( ctx , cycle , sync , tags ) ;
2006-12-20 03:58:27 +03:00
}
int
fw_iso_context_queue ( struct fw_iso_context * ctx ,
2007-02-17 01:34:38 +03:00
struct fw_iso_packet * packet ,
struct fw_iso_buffer * buffer ,
unsigned long payload )
2006-12-20 03:58:27 +03:00
{
struct fw_card * card = ctx - > card ;
2007-02-17 01:34:38 +03:00
return card - > driver - > queue_iso ( ctx , packet , buffer , payload ) ;
2006-12-20 03:58:27 +03:00
}
2007-02-17 01:34:42 +03:00
int
fw_iso_context_stop ( struct fw_iso_context * ctx )
{
return ctx - > card - > driver - > stop_iso ( ctx ) ;
}