2009-11-03 11:45:58 -08:00
/*
* Copyright ( c ) 2009 Intel Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Maintained at www . Open - FCoE . org
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/scatterlist.h>
# include <linux/crc32.h>
# include <scsi/libfc.h>
2010-07-20 15:21:01 -07:00
# include <scsi/fc_encode.h>
2009-11-03 11:45:58 -08:00
# include "fc_libfc.h"
MODULE_AUTHOR ( " Open-FCoE.org " ) ;
MODULE_DESCRIPTION ( " libfc " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
unsigned int fc_debug_logging ;
module_param_named ( debug_logging , fc_debug_logging , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug_logging , " a bit mask of logging levels " ) ;
2009-11-03 11:46:03 -08:00
/**
* libfc_init ( ) - Initialize libfc . ko
*/
static int __init libfc_init ( void )
{
int rc = 0 ;
rc = fc_setup_fcp ( ) ;
if ( rc )
return rc ;
rc = fc_setup_exch_mgr ( ) ;
if ( rc )
goto destroy_pkt_cache ;
rc = fc_setup_rport ( ) ;
if ( rc )
goto destroy_em ;
return rc ;
destroy_em :
fc_destroy_exch_mgr ( ) ;
destroy_pkt_cache :
fc_destroy_fcp ( ) ;
return rc ;
}
module_init ( libfc_init ) ;
/**
* libfc_exit ( ) - Tear down libfc . ko
*/
static void __exit libfc_exit ( void )
{
fc_destroy_fcp ( ) ;
fc_destroy_exch_mgr ( ) ;
fc_destroy_rport ( ) ;
}
module_exit ( libfc_exit ) ;
2009-11-03 11:47:28 -08:00
/**
* fc_copy_buffer_to_sglist ( ) - This routine copies the data of a buffer
2009-11-03 11:47:39 -08:00
* into a scatter - gather list ( SG list ) .
2009-11-03 11:47:28 -08:00
*
* @ buf : pointer to the data buffer .
* @ len : the byte - length of the data buffer .
* @ sg : pointer to the pointer of the SG list .
* @ nents : pointer to the remaining number of entries in the SG list .
* @ offset : pointer to the current offset in the SG list .
* @ km_type : dedicated page table slot type for kmap_atomic .
* @ crc : pointer to the 32 - bit crc value .
2009-11-03 11:47:39 -08:00
* If crc is NULL , CRC is not calculated .
2009-11-03 11:47:28 -08:00
*/
u32 fc_copy_buffer_to_sglist ( void * buf , size_t len ,
struct scatterlist * sg ,
u32 * nents , size_t * offset ,
enum km_type km_type , u32 * crc )
{
size_t remaining = len ;
u32 copy_len = 0 ;
while ( remaining > 0 & & sg ) {
size_t off , sg_bytes ;
void * page_addr ;
if ( * offset > = sg - > length ) {
/*
* Check for end and drop resources
* from the last iteration .
*/
if ( ! ( * nents ) )
break ;
- - ( * nents ) ;
* offset - = sg - > length ;
sg = sg_next ( sg ) ;
continue ;
}
sg_bytes = min ( remaining , sg - > length - * offset ) ;
/*
* The scatterlist item may be bigger than PAGE_SIZE ,
* but we are limited to mapping PAGE_SIZE at a time .
*/
off = * offset + sg - > offset ;
sg_bytes = min ( sg_bytes ,
( size_t ) ( PAGE_SIZE - ( off & ~ PAGE_MASK ) ) ) ;
page_addr = kmap_atomic ( sg_page ( sg ) + ( off > > PAGE_SHIFT ) ,
km_type ) ;
if ( crc )
* crc = crc32 ( * crc , buf , sg_bytes ) ;
memcpy ( ( char * ) page_addr + ( off & ~ PAGE_MASK ) , buf , sg_bytes ) ;
kunmap_atomic ( page_addr , km_type ) ;
buf + = sg_bytes ;
* offset + = sg_bytes ;
remaining - = sg_bytes ;
copy_len + = sg_bytes ;
}
return copy_len ;
}
2010-07-20 15:21:01 -07:00
/**
* fc_fill_hdr ( ) - fill FC header fields based on request
* @ fp : reply frame containing header to be filled in
* @ in_fp : request frame containing header to use in filling in reply
* @ r_ctl : R_CTL value for header
* @ f_ctl : F_CTL value for header , with 0 pad
* @ seq_cnt : sequence count for the header , ignored if frame has a sequence
* @ parm_offset : parameter / offset value
*/
void fc_fill_hdr ( struct fc_frame * fp , const struct fc_frame * in_fp ,
enum fc_rctl r_ctl , u32 f_ctl , u16 seq_cnt , u32 parm_offset )
{
struct fc_frame_header * fh ;
struct fc_frame_header * in_fh ;
struct fc_seq * sp ;
u32 fill ;
fh = __fc_frame_header_get ( fp ) ;
in_fh = __fc_frame_header_get ( in_fp ) ;
if ( f_ctl & FC_FC_END_SEQ ) {
fill = - fr_len ( fp ) & 3 ;
if ( fill ) {
/* TODO, this may be a problem with fragmented skb */
memset ( skb_put ( fp_skb ( fp ) , fill ) , 0 , fill ) ;
f_ctl | = fill ;
}
fr_eof ( fp ) = FC_EOF_T ;
} else {
WARN_ON ( fr_len ( fp ) % 4 ! = 0 ) ; /* no pad to non last frame */
fr_eof ( fp ) = FC_EOF_N ;
}
fh - > fh_r_ctl = r_ctl ;
memcpy ( fh - > fh_d_id , in_fh - > fh_s_id , sizeof ( fh - > fh_d_id ) ) ;
memcpy ( fh - > fh_s_id , in_fh - > fh_d_id , sizeof ( fh - > fh_s_id ) ) ;
fh - > fh_type = in_fh - > fh_type ;
hton24 ( fh - > fh_f_ctl , f_ctl ) ;
fh - > fh_ox_id = in_fh - > fh_ox_id ;
fh - > fh_rx_id = in_fh - > fh_rx_id ;
fh - > fh_cs_ctl = 0 ;
fh - > fh_df_ctl = 0 ;
fh - > fh_parm_offset = htonl ( parm_offset ) ;
sp = fr_seq ( in_fp ) ;
if ( sp ) {
fr_seq ( fp ) = sp ;
fh - > fh_seq_id = sp - > id ;
seq_cnt = sp - > cnt ;
} else {
fh - > fh_seq_id = 0 ;
}
fh - > fh_seq_cnt = ntohs ( seq_cnt ) ;
fr_sof ( fp ) = seq_cnt ? FC_SOF_N3 : FC_SOF_I3 ;
fr_encaps ( fp ) = fr_encaps ( in_fp ) ;
}
EXPORT_SYMBOL ( fc_fill_hdr ) ;
/**
* fc_fill_reply_hdr ( ) - fill FC reply header fields based on request
* @ fp : reply frame containing header to be filled in
* @ in_fp : request frame containing header to use in filling in reply
* @ r_ctl : R_CTL value for reply
* @ parm_offset : parameter / offset value
*/
void fc_fill_reply_hdr ( struct fc_frame * fp , const struct fc_frame * in_fp ,
enum fc_rctl r_ctl , u32 parm_offset )
{
struct fc_seq * sp ;
sp = fr_seq ( in_fp ) ;
if ( sp )
fr_seq ( fp ) = fr_dev ( in_fp ) - > tt . seq_start_next ( sp ) ;
fc_fill_hdr ( fp , in_fp , r_ctl , FC_FCTL_RESP , 0 , parm_offset ) ;
}
EXPORT_SYMBOL ( fc_fill_reply_hdr ) ;