2012-02-07 16:18:40 +04:00
/******************************************************************************
*
* This file is provided under a dual BSD / GPLv2 license . When using or
* redistributing this file , you may do so under either license .
*
* GPL LICENSE SUMMARY
*
2013-01-22 00:39:26 +04:00
* Copyright ( c ) 2007 - 2013 Intel Corporation . All rights reserved .
2012-02-07 16:18:40 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 ,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE . GPL .
*
* Contact Information :
* Intel Linux Wireless < ilw @ linux . intel . com >
* Intel Corporation , 5200 N . E . Elam Young Parkway , Hillsboro , OR 97124 - 6497
*
* BSD LICENSE
*
2013-01-22 00:39:26 +04:00
* Copyright ( c ) 2005 - 2013 Intel Corporation . All rights reserved .
2012-02-07 16:18:40 +04:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/completion.h>
2012-03-05 23:24:50 +04:00
# include <linux/dma-mapping.h>
# include <linux/firmware.h>
# include <linux/module.h>
2012-09-10 13:50:18 +04:00
# include <linux/vmalloc.h>
2012-02-07 16:18:40 +04:00
# include "iwl-drv.h"
2012-04-19 11:29:58 +04:00
# include "iwl-debug.h"
2012-02-07 16:18:40 +04:00
# include "iwl-trans.h"
2012-02-09 18:08:15 +04:00
# include "iwl-op-mode.h"
2012-03-11 01:00:12 +04:00
# include "iwl-agn-hw.h"
2012-04-02 17:04:33 +04:00
# include "iwl-fw.h"
# include "iwl-config.h"
2012-04-17 18:36:30 +04:00
# include "iwl-modparams.h"
2012-02-07 16:18:40 +04:00
2012-03-07 01:30:37 +04:00
/* private includes */
2012-03-07 01:30:44 +04:00
# include "iwl-fw-file.h"
2012-03-07 01:30:37 +04:00
2012-05-17 00:54:27 +04:00
/******************************************************************************
*
* module boiler plate
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* module name , copyright , version , etc .
*/
# define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux"
# ifdef CONFIG_IWLWIFI_DEBUG
# define VD "d"
# else
# define VD
# endif
# define DRV_VERSION IWLWIFI_VERSION VD
MODULE_DESCRIPTION ( DRV_DESCRIPTION ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( DRV_COPYRIGHT " " DRV_AUTHOR ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
static struct dentry * iwl_dbgfs_root ;
# endif
2012-03-07 01:30:38 +04:00
/**
* struct iwl_drv - drv common data
2012-05-17 00:54:27 +04:00
* @ list : list of drv structures using this opmode
2012-03-07 01:30:38 +04:00
* @ fw : the iwl_fw structure
* @ op_mode : the running op_mode
2012-03-22 19:51:44 +04:00
* @ trans : transport layer
2012-03-23 01:59:52 +04:00
* @ dev : for debug prints only
2012-03-22 19:51:44 +04:00
* @ cfg : configuration struct
2012-03-07 01:30:38 +04:00
* @ fw_index : firmware revision to try loading
* @ firmware_name : composite filename of ucode file to load
* @ request_firmware_complete : the firmware has been obtained from user space
*/
struct iwl_drv {
2012-05-17 00:54:27 +04:00
struct list_head list ;
2012-03-07 01:30:38 +04:00
struct iwl_fw fw ;
struct iwl_op_mode * op_mode ;
2012-03-26 19:51:09 +04:00
struct iwl_trans * trans ;
2012-03-23 01:59:52 +04:00
struct device * dev ;
2012-03-22 19:51:44 +04:00
const struct iwl_cfg * cfg ;
2012-03-07 01:30:38 +04:00
int fw_index ; /* firmware we're trying to load */
char firmware_name [ 25 ] ; /* name of firmware file to load */
struct completion request_firmware_complete ;
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
struct dentry * dbgfs_drv ;
struct dentry * dbgfs_trans ;
struct dentry * dbgfs_op_mode ;
# endif
2012-03-07 01:30:38 +04:00
} ;
2012-05-17 00:54:27 +04:00
# define DVM_OP_MODE 0
# define MVM_OP_MODE 1
2012-03-07 01:30:38 +04:00
2012-06-06 11:42:57 +04:00
/* Protects the table contents, i.e. the ops pointer & drv list */
static struct mutex iwlwifi_opmode_table_mtx ;
2012-05-17 00:54:27 +04:00
static struct iwlwifi_opmode_table {
const char * name ; /* name: iwldvm, iwlmvm, etc */
const struct iwl_op_mode_ops * ops ; /* pointer to op_mode ops */
struct list_head drv ; /* list of devices using this op_mode */
} iwlwifi_opmode_table [ ] = { /* ops set when driver is initialized */
{ . name = " iwldvm " , . ops = NULL } ,
{ . name = " iwlmvm " , . ops = NULL } ,
} ;
2012-03-07 01:30:38 +04:00
2012-03-11 01:00:12 +04:00
/*
* struct fw_sec : Just for the image parsing proccess .
* For the fw storage we are using struct fw_desc .
*/
struct fw_sec {
const void * data ; /* the sec data */
size_t size ; /* section size */
u32 offset ; /* offset of writing in the device */
} ;
2012-03-07 01:30:38 +04:00
static void iwl_free_fw_desc ( struct iwl_drv * drv , struct fw_desc * desc )
2012-03-05 23:24:50 +04:00
{
2012-09-10 13:50:18 +04:00
vfree ( desc - > data ) ;
desc - > data = NULL ;
2012-03-05 23:24:50 +04:00
desc - > len = 0 ;
}
2012-03-07 01:30:38 +04:00
static void iwl_free_fw_img ( struct iwl_drv * drv , struct fw_img * img )
2012-03-05 23:24:50 +04:00
{
2012-03-11 01:00:14 +04:00
int i ;
for ( i = 0 ; i < IWL_UCODE_SECTION_MAX ; i + + )
iwl_free_fw_desc ( drv , & img - > sec [ i ] ) ;
2012-03-05 23:24:50 +04:00
}
2012-03-07 01:30:38 +04:00
static void iwl_dealloc_ucode ( struct iwl_drv * drv )
2012-03-05 23:24:50 +04:00
{
2012-03-11 01:00:14 +04:00
int i ;
for ( i = 0 ; i < IWL_UCODE_TYPE_MAX ; i + + )
iwl_free_fw_img ( drv , drv - > fw . img + i ) ;
2012-03-05 23:24:50 +04:00
}
2012-03-07 01:30:38 +04:00
static int iwl_alloc_fw_desc ( struct iwl_drv * drv , struct fw_desc * desc ,
2012-09-10 13:50:18 +04:00
struct fw_sec * sec )
2012-03-05 23:24:50 +04:00
{
2012-09-10 13:50:18 +04:00
void * data ;
desc - > data = NULL ;
if ( ! sec | | ! sec - > size )
2012-03-05 23:24:50 +04:00
return - EINVAL ;
2012-09-10 13:50:18 +04:00
data = vmalloc ( sec - > size ) ;
if ( ! data )
2012-03-05 23:24:50 +04:00
return - ENOMEM ;
2012-03-11 01:00:12 +04:00
desc - > len = sec - > size ;
desc - > offset = sec - > offset ;
2012-09-10 13:50:18 +04:00
memcpy ( data , sec - > data , desc - > len ) ;
desc - > data = data ;
2012-03-05 23:24:50 +04:00
return 0 ;
}
2012-07-16 13:31:28 +04:00
static void iwl_req_fw_callback ( const struct firmware * ucode_raw ,
void * context ) ;
2012-03-05 23:24:50 +04:00
# define UCODE_EXPERIMENTAL_INDEX 100
# define UCODE_EXPERIMENTAL_TAG "exp"
2012-03-07 01:30:38 +04:00
static int iwl_request_firmware ( struct iwl_drv * drv , bool first )
2012-03-05 23:24:50 +04:00
{
2012-03-22 19:51:44 +04:00
const char * name_pre = drv - > cfg - > fw_name_pre ;
2012-03-05 23:24:50 +04:00
char tag [ 8 ] ;
if ( first ) {
# ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
2012-03-07 01:30:38 +04:00
drv - > fw_index = UCODE_EXPERIMENTAL_INDEX ;
2012-03-05 23:24:50 +04:00
strcpy ( tag , UCODE_EXPERIMENTAL_TAG ) ;
2012-03-07 01:30:38 +04:00
} else if ( drv - > fw_index = = UCODE_EXPERIMENTAL_INDEX ) {
2012-03-05 23:24:50 +04:00
# endif
2012-03-22 19:51:44 +04:00
drv - > fw_index = drv - > cfg - > ucode_api_max ;
2012-03-07 01:30:38 +04:00
sprintf ( tag , " %d " , drv - > fw_index ) ;
2012-03-05 23:24:50 +04:00
} else {
2012-03-07 01:30:38 +04:00
drv - > fw_index - - ;
sprintf ( tag , " %d " , drv - > fw_index ) ;
2012-03-05 23:24:50 +04:00
}
2012-03-22 19:51:44 +04:00
if ( drv - > fw_index < drv - > cfg - > ucode_api_min ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " no suitable firmware found! \n " ) ;
2012-03-05 23:24:50 +04:00
return - ENOENT ;
}
2012-03-07 01:30:38 +04:00
sprintf ( drv - > firmware_name , " %s%s%s " , name_pre , tag , " .ucode " ) ;
2012-03-05 23:24:50 +04:00
2012-03-07 01:30:38 +04:00
IWL_DEBUG_INFO ( drv , " attempting to load firmware %s'%s' \n " ,
( drv - > fw_index = = UCODE_EXPERIMENTAL_INDEX )
2012-03-05 23:24:50 +04:00
? " EXPERIMENTAL " : " " ,
2012-03-07 01:30:38 +04:00
drv - > firmware_name ) ;
2012-03-05 23:24:50 +04:00
2012-03-07 01:30:38 +04:00
return request_firmware_nowait ( THIS_MODULE , 1 , drv - > firmware_name ,
2012-03-22 19:51:44 +04:00
drv - > trans - > dev ,
2012-07-16 13:31:28 +04:00
GFP_KERNEL , drv , iwl_req_fw_callback ) ;
2012-03-05 23:24:50 +04:00
}
2012-03-11 01:00:12 +04:00
struct fw_img_parsing {
2012-03-11 01:00:14 +04:00
struct fw_sec sec [ IWL_UCODE_SECTION_MAX ] ;
2012-03-11 01:00:12 +04:00
int sec_counter ;
} ;
2012-03-11 01:00:13 +04:00
/*
* struct fw_sec_parsing : to extract fw section and it ' s offset from tlv
*/
struct fw_sec_parsing {
__le32 offset ;
const u8 data [ ] ;
} __packed ;
/**
* struct iwl_tlv_calib_data - parse the default calib data from TLV
*
* @ ucode_type : the uCode to which the following default calib relates .
* @ calib : default calibrations .
*/
struct iwl_tlv_calib_data {
__le32 ucode_type ;
__le64 calib ;
} __packed ;
2012-03-11 01:00:12 +04:00
struct iwl_firmware_pieces {
struct fw_img_parsing img [ IWL_UCODE_TYPE_MAX ] ;
2012-03-05 23:24:50 +04:00
u32 init_evtlog_ptr , init_evtlog_size , init_errlog_ptr ;
u32 inst_evtlog_ptr , inst_evtlog_size , inst_errlog_ptr ;
} ;
2012-03-11 01:00:12 +04:00
/*
* These functions are just to extract uCode section data from the pieces
* structure .
*/
static struct fw_sec * get_sec ( struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type ,
int sec )
{
return & pieces - > img [ type ] . sec [ sec ] ;
}
static void set_sec_data ( struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type ,
int sec ,
const void * data )
{
pieces - > img [ type ] . sec [ sec ] . data = data ;
}
static void set_sec_size ( struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type ,
int sec ,
size_t size )
{
pieces - > img [ type ] . sec [ sec ] . size = size ;
}
static size_t get_sec_size ( struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type ,
int sec )
{
return pieces - > img [ type ] . sec [ sec ] . size ;
}
static void set_sec_offset ( struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type ,
int sec ,
u32 offset )
{
pieces - > img [ type ] . sec [ sec ] . offset = offset ;
}
2012-03-11 01:00:13 +04:00
/*
* Gets uCode section from tlv .
*/
static int iwl_store_ucode_sec ( struct iwl_firmware_pieces * pieces ,
const void * data , enum iwl_ucode_type type ,
int size )
{
struct fw_img_parsing * img ;
struct fw_sec * sec ;
struct fw_sec_parsing * sec_parse ;
if ( WARN_ON ( ! pieces | | ! data | | type > = IWL_UCODE_TYPE_MAX ) )
return - 1 ;
sec_parse = ( struct fw_sec_parsing * ) data ;
img = & pieces - > img [ type ] ;
sec = & img - > sec [ img - > sec_counter ] ;
sec - > offset = le32_to_cpu ( sec_parse - > offset ) ;
sec - > data = sec_parse - > data ;
2012-03-13 16:32:48 +04:00
sec - > size = size - sizeof ( sec_parse - > offset ) ;
2012-03-11 01:00:13 +04:00
+ + img - > sec_counter ;
return 0 ;
}
static int iwl_set_default_calib ( struct iwl_drv * drv , const u8 * data )
{
struct iwl_tlv_calib_data * def_calib =
( struct iwl_tlv_calib_data * ) data ;
u32 ucode_type = le32_to_cpu ( def_calib - > ucode_type ) ;
if ( ucode_type > = IWL_UCODE_TYPE_MAX ) {
IWL_ERR ( drv , " Wrong ucode_type %u for default calibration. \n " ,
ucode_type ) ;
return - EINVAL ;
}
drv - > fw . default_calib [ ucode_type ] = le64_to_cpu ( def_calib - > calib ) ;
return 0 ;
}
2012-03-07 01:30:38 +04:00
static int iwl_parse_v1_v2_firmware ( struct iwl_drv * drv ,
2012-03-11 01:00:12 +04:00
const struct firmware * ucode_raw ,
struct iwl_firmware_pieces * pieces )
2012-03-05 23:24:50 +04:00
{
struct iwl_ucode_header * ucode = ( void * ) ucode_raw - > data ;
u32 api_ver , hdr_size , build ;
char buildstr [ 25 ] ;
const u8 * src ;
2012-03-07 01:30:38 +04:00
drv - > fw . ucode_ver = le32_to_cpu ( ucode - > ver ) ;
api_ver = IWL_UCODE_API ( drv - > fw . ucode_ver ) ;
2012-03-05 23:24:50 +04:00
switch ( api_ver ) {
default :
hdr_size = 28 ;
if ( ucode_raw - > size < hdr_size ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " File size too small! \n " ) ;
2012-03-05 23:24:50 +04:00
return - EINVAL ;
}
build = le32_to_cpu ( ucode - > u . v2 . build ) ;
2012-03-11 01:00:12 +04:00
set_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ,
le32_to_cpu ( ucode - > u . v2 . inst_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ,
le32_to_cpu ( ucode - > u . v2 . data_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ,
le32_to_cpu ( ucode - > u . v2 . init_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ,
le32_to_cpu ( ucode - > u . v2 . init_data_size ) ) ;
2012-03-05 23:24:50 +04:00
src = ucode - > u . v2 . data ;
break ;
case 0 :
case 1 :
case 2 :
hdr_size = 24 ;
if ( ucode_raw - > size < hdr_size ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " File size too small! \n " ) ;
2012-03-05 23:24:50 +04:00
return - EINVAL ;
}
build = 0 ;
2012-03-11 01:00:12 +04:00
set_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ,
le32_to_cpu ( ucode - > u . v1 . inst_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ,
le32_to_cpu ( ucode - > u . v1 . data_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ,
le32_to_cpu ( ucode - > u . v1 . init_size ) ) ;
set_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ,
le32_to_cpu ( ucode - > u . v1 . init_data_size ) ) ;
2012-03-05 23:24:50 +04:00
src = ucode - > u . v1 . data ;
break ;
}
if ( build )
sprintf ( buildstr , " build %u%s " , build ,
2012-03-07 01:30:38 +04:00
( drv - > fw_index = = UCODE_EXPERIMENTAL_INDEX )
2012-03-05 23:24:50 +04:00
? " (EXP) " : " " ) ;
else
buildstr [ 0 ] = ' \0 ' ;
2012-03-07 01:30:38 +04:00
snprintf ( drv - > fw . fw_version ,
sizeof ( drv - > fw . fw_version ) ,
2012-03-05 23:24:50 +04:00
" %u.%u.%u.%u%s " ,
2012-03-07 01:30:38 +04:00
IWL_UCODE_MAJOR ( drv - > fw . ucode_ver ) ,
IWL_UCODE_MINOR ( drv - > fw . ucode_ver ) ,
IWL_UCODE_API ( drv - > fw . ucode_ver ) ,
IWL_UCODE_SERIAL ( drv - > fw . ucode_ver ) ,
2012-03-05 23:24:50 +04:00
buildstr ) ;
/* Verify size of file vs. image size info in file's header */
2012-03-11 01:00:12 +04:00
if ( ucode_raw - > size ! = hdr_size +
get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ) +
get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ) +
get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ) +
get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ) ) {
2012-03-05 23:24:50 +04:00
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv ,
2012-03-05 23:24:50 +04:00
" uCode file size %d does not match expected size \n " ,
( int ) ucode_raw - > size ) ;
return - EINVAL ;
}
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST , src ) ;
src + = get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ) ;
set_sec_offset ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ,
IWLAGN_RTC_INST_LOWER_BOUND ) ;
set_sec_data ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA , src ) ;
src + = get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ) ;
set_sec_offset ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ,
IWLAGN_RTC_DATA_LOWER_BOUND ) ;
set_sec_data ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST , src ) ;
src + = get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ) ;
set_sec_offset ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ,
IWLAGN_RTC_INST_LOWER_BOUND ) ;
set_sec_data ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA , src ) ;
src + = get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ) ;
set_sec_offset ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ,
IWLAGN_RTC_DATA_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
return 0 ;
}
2012-03-07 01:30:38 +04:00
static int iwl_parse_tlv_firmware ( struct iwl_drv * drv ,
2012-03-05 23:24:50 +04:00
const struct firmware * ucode_raw ,
2012-03-11 01:00:12 +04:00
struct iwl_firmware_pieces * pieces ,
2012-03-05 23:24:50 +04:00
struct iwl_ucode_capabilities * capa )
{
struct iwl_tlv_ucode_header * ucode = ( void * ) ucode_raw - > data ;
struct iwl_ucode_tlv * tlv ;
size_t len = ucode_raw - > size ;
const u8 * data ;
u32 tlv_len ;
enum iwl_ucode_tlv_type tlv_type ;
const u8 * tlv_data ;
char buildstr [ 25 ] ;
u32 build ;
if ( len < sizeof ( * ucode ) ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " uCode has invalid length: %zd \n " , len ) ;
2012-03-05 23:24:50 +04:00
return - EINVAL ;
}
if ( ucode - > magic ! = cpu_to_le32 ( IWL_TLV_UCODE_MAGIC ) ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " invalid uCode magic: 0X%x \n " ,
2012-03-05 23:24:50 +04:00
le32_to_cpu ( ucode - > magic ) ) ;
return - EINVAL ;
}
2012-03-07 01:30:38 +04:00
drv - > fw . ucode_ver = le32_to_cpu ( ucode - > ver ) ;
2012-03-05 23:24:50 +04:00
build = le32_to_cpu ( ucode - > build ) ;
if ( build )
sprintf ( buildstr , " build %u%s " , build ,
2012-03-07 01:30:38 +04:00
( drv - > fw_index = = UCODE_EXPERIMENTAL_INDEX )
2012-03-05 23:24:50 +04:00
? " (EXP) " : " " ) ;
else
buildstr [ 0 ] = ' \0 ' ;
2012-03-07 01:30:38 +04:00
snprintf ( drv - > fw . fw_version ,
sizeof ( drv - > fw . fw_version ) ,
2012-03-05 23:24:50 +04:00
" %u.%u.%u.%u%s " ,
2012-03-07 01:30:38 +04:00
IWL_UCODE_MAJOR ( drv - > fw . ucode_ver ) ,
IWL_UCODE_MINOR ( drv - > fw . ucode_ver ) ,
IWL_UCODE_API ( drv - > fw . ucode_ver ) ,
IWL_UCODE_SERIAL ( drv - > fw . ucode_ver ) ,
2012-03-05 23:24:50 +04:00
buildstr ) ;
data = ucode - > data ;
len - = sizeof ( * ucode ) ;
while ( len > = sizeof ( * tlv ) ) {
len - = sizeof ( * tlv ) ;
tlv = ( void * ) data ;
tlv_len = le32_to_cpu ( tlv - > length ) ;
2012-03-09 12:16:35 +04:00
tlv_type = le32_to_cpu ( tlv - > type ) ;
2012-03-05 23:24:50 +04:00
tlv_data = tlv - > data ;
if ( len < tlv_len ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " invalid TLV len: %zd/%u \n " ,
2012-03-05 23:24:50 +04:00
len , tlv_len ) ;
return - EINVAL ;
}
len - = ALIGN ( tlv_len , 4 ) ;
data + = sizeof ( * tlv ) + ALIGN ( tlv_len , 4 ) ;
switch ( tlv_type ) {
case IWL_UCODE_TLV_INST :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_INST , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_INST , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_INST ,
IWLAGN_RTC_INST_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_DATA :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA ,
IWLAGN_RTC_DATA_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_INIT :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_INST , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_INST , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_INST ,
IWLAGN_RTC_INST_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_INIT_DATA :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_DATA , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_DATA , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_DATA ,
IWLAGN_RTC_DATA_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_BOOT :
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " Found unexpected BOOT ucode \n " ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_PROBE_MAX_LEN :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
capa - > max_probe_length =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_PAN :
if ( tlv_len )
goto invalid_tlv_len ;
capa - > flags | = IWL_UCODE_TLV_FLAGS_PAN ;
break ;
case IWL_UCODE_TLV_FLAGS :
/* must be at least one u32 */
if ( tlv_len < sizeof ( u32 ) )
goto invalid_tlv_len ;
/* and a proper number of u32s */
if ( tlv_len % sizeof ( u32 ) )
goto invalid_tlv_len ;
/*
* This driver only reads the first u32 as
* right now no more features are defined ,
* if that changes then either the driver
* will not work with the new firmware , or
* it ' ll not take advantage of new features .
*/
capa - > flags = le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_INIT_EVTLOG_PTR :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > init_evtlog_ptr =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_INIT_EVTLOG_SIZE :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > init_evtlog_size =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_INIT_ERRLOG_PTR :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > init_errlog_ptr =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_RUNT_EVTLOG_PTR :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > inst_evtlog_ptr =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > inst_evtlog_size =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_RUNT_ERRLOG_PTR :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
pieces - > inst_errlog_ptr =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
case IWL_UCODE_TLV_ENHANCE_SENS_TBL :
if ( tlv_len )
goto invalid_tlv_len ;
2012-03-07 01:30:38 +04:00
drv - > fw . enhance_sensitivity_table = true ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_WOWLAN_INST :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_INST , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_INST , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_INST ,
IWLAGN_RTC_INST_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_WOWLAN_DATA :
2012-03-11 01:00:12 +04:00
set_sec_data ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_DATA , tlv_data ) ;
set_sec_size ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_DATA , tlv_len ) ;
set_sec_offset ( pieces , IWL_UCODE_WOWLAN ,
IWL_UCODE_SECTION_DATA ,
IWLAGN_RTC_DATA_LOWER_BOUND ) ;
2012-03-05 23:24:50 +04:00
break ;
case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
capa - > standard_phy_calibration_size =
le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
2012-03-11 01:00:13 +04:00
case IWL_UCODE_TLV_SEC_RT :
iwl_store_ucode_sec ( pieces , tlv_data , IWL_UCODE_REGULAR ,
tlv_len ) ;
2012-03-11 01:00:15 +04:00
drv - > fw . mvm_fw = true ;
2012-03-11 01:00:13 +04:00
break ;
case IWL_UCODE_TLV_SEC_INIT :
iwl_store_ucode_sec ( pieces , tlv_data , IWL_UCODE_INIT ,
tlv_len ) ;
2012-03-11 01:00:15 +04:00
drv - > fw . mvm_fw = true ;
2012-03-11 01:00:13 +04:00
break ;
case IWL_UCODE_TLV_SEC_WOWLAN :
iwl_store_ucode_sec ( pieces , tlv_data , IWL_UCODE_WOWLAN ,
tlv_len ) ;
2012-03-11 01:00:15 +04:00
drv - > fw . mvm_fw = true ;
2012-03-11 01:00:13 +04:00
break ;
case IWL_UCODE_TLV_DEF_CALIB :
if ( tlv_len ! = sizeof ( struct iwl_tlv_calib_data ) )
goto invalid_tlv_len ;
if ( iwl_set_default_calib ( drv , tlv_data ) )
goto tlv_error ;
break ;
case IWL_UCODE_TLV_PHY_SKU :
if ( tlv_len ! = sizeof ( u32 ) )
goto invalid_tlv_len ;
drv - > fw . phy_config = le32_to_cpup ( ( __le32 * ) tlv_data ) ;
break ;
2012-03-05 23:24:50 +04:00
default :
2012-03-07 01:30:38 +04:00
IWL_DEBUG_INFO ( drv , " unknown TLV: %d \n " , tlv_type ) ;
2012-03-05 23:24:50 +04:00
break ;
}
}
if ( len ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " invalid TLV after parsing: %zd \n " , len ) ;
iwl_print_hex_dump ( drv , IWL_DL_FW , ( u8 * ) data , len ) ;
2012-03-05 23:24:50 +04:00
return - EINVAL ;
}
return 0 ;
invalid_tlv_len :
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " TLV %d has invalid size: %u \n " , tlv_type , tlv_len ) ;
2012-03-11 01:00:13 +04:00
tlv_error :
2012-03-07 01:30:38 +04:00
iwl_print_hex_dump ( drv , IWL_DL_FW , tlv_data , tlv_len ) ;
2012-03-05 23:24:50 +04:00
return - EINVAL ;
}
2012-05-17 00:49:49 +04:00
static int iwl_alloc_ucode ( struct iwl_drv * drv ,
struct iwl_firmware_pieces * pieces ,
enum iwl_ucode_type type )
2012-03-11 01:00:14 +04:00
{
int i ;
for ( i = 0 ;
i < IWL_UCODE_SECTION_MAX & & get_sec_size ( pieces , type , i ) ;
i + + )
if ( iwl_alloc_fw_desc ( drv , & ( drv - > fw . img [ type ] . sec [ i ] ) ,
2012-05-17 00:49:49 +04:00
get_sec ( pieces , type , i ) ) )
return - ENOMEM ;
2012-03-11 01:00:14 +04:00
return 0 ;
}
static int validate_sec_sizes ( struct iwl_drv * drv ,
struct iwl_firmware_pieces * pieces ,
const struct iwl_cfg * cfg )
{
IWL_DEBUG_INFO ( drv , " f/w package hdr runtime inst size = %Zd \n " ,
get_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_INST ) ) ;
IWL_DEBUG_INFO ( drv , " f/w package hdr runtime data size = %Zd \n " ,
get_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA ) ) ;
IWL_DEBUG_INFO ( drv , " f/w package hdr init inst size = %Zd \n " ,
get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ) ) ;
IWL_DEBUG_INFO ( drv , " f/w package hdr init data size = %Zd \n " ,
get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ) ) ;
/* Verify that uCode images will fit in card's SRAM. */
if ( get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_INST ) >
cfg - > max_inst_size ) {
IWL_ERR ( drv , " uCode instr len %Zd too large to fit in \n " ,
get_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_INST ) ) ;
return - 1 ;
}
if ( get_sec_size ( pieces , IWL_UCODE_REGULAR , IWL_UCODE_SECTION_DATA ) >
cfg - > max_data_size ) {
IWL_ERR ( drv , " uCode data len %Zd too large to fit in \n " ,
get_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA ) ) ;
return - 1 ;
}
if ( get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_INST ) >
cfg - > max_inst_size ) {
IWL_ERR ( drv , " uCode init instr len %Zd too large to fit in \n " ,
get_sec_size ( pieces , IWL_UCODE_INIT ,
IWL_UCODE_SECTION_INST ) ) ;
return - 1 ;
}
if ( get_sec_size ( pieces , IWL_UCODE_INIT , IWL_UCODE_SECTION_DATA ) >
cfg - > max_data_size ) {
IWL_ERR ( drv , " uCode init data len %Zd too large to fit in \n " ,
get_sec_size ( pieces , IWL_UCODE_REGULAR ,
IWL_UCODE_SECTION_DATA ) ) ;
return - 1 ;
}
return 0 ;
}
2012-07-17 05:43:56 +04:00
static struct iwl_op_mode *
_iwl_op_mode_start ( struct iwl_drv * drv , struct iwlwifi_opmode_table * op )
{
const struct iwl_op_mode_ops * ops = op - > ops ;
struct dentry * dbgfs_dir = NULL ;
struct iwl_op_mode * op_mode = NULL ;
# ifdef CONFIG_IWLWIFI_DEBUGFS
drv - > dbgfs_op_mode = debugfs_create_dir ( op - > name ,
drv - > dbgfs_drv ) ;
if ( ! drv - > dbgfs_op_mode ) {
IWL_ERR ( drv ,
" failed to create opmode debugfs directory \n " ) ;
return op_mode ;
}
dbgfs_dir = drv - > dbgfs_op_mode ;
# endif
op_mode = ops - > start ( drv - > trans , drv - > cfg , & drv - > fw , dbgfs_dir ) ;
# ifdef CONFIG_IWLWIFI_DEBUGFS
if ( ! op_mode ) {
debugfs_remove_recursive ( drv - > dbgfs_op_mode ) ;
drv - > dbgfs_op_mode = NULL ;
}
# endif
return op_mode ;
}
static void _iwl_op_mode_stop ( struct iwl_drv * drv )
{
/* op_mode can be NULL if its start failed */
if ( drv - > op_mode ) {
iwl_op_mode_stop ( drv - > op_mode ) ;
drv - > op_mode = NULL ;
# ifdef CONFIG_IWLWIFI_DEBUGFS
debugfs_remove_recursive ( drv - > dbgfs_op_mode ) ;
drv - > dbgfs_op_mode = NULL ;
# endif
}
}
2012-03-05 23:24:50 +04:00
/**
2012-07-16 13:31:28 +04:00
* iwl_req_fw_callback - callback when firmware was loaded
2012-03-05 23:24:50 +04:00
*
* If loaded successfully , copies the firmware into buffers
* for the card to fetch ( via DMA ) .
*/
2012-07-16 13:31:28 +04:00
static void iwl_req_fw_callback ( const struct firmware * ucode_raw , void * context )
2012-03-05 23:24:50 +04:00
{
2012-03-07 01:30:38 +04:00
struct iwl_drv * drv = context ;
struct iwl_fw * fw = & drv - > fw ;
2012-03-05 23:24:50 +04:00
struct iwl_ucode_header * ucode ;
2012-05-17 00:54:27 +04:00
struct iwlwifi_opmode_table * op ;
2012-03-05 23:24:50 +04:00
int err ;
2012-03-11 01:00:12 +04:00
struct iwl_firmware_pieces pieces ;
2012-03-22 19:51:44 +04:00
const unsigned int api_max = drv - > cfg - > ucode_api_max ;
unsigned int api_ok = drv - > cfg - > ucode_api_ok ;
const unsigned int api_min = drv - > cfg - > ucode_api_min ;
2012-03-05 23:24:50 +04:00
u32 api_ver ;
2012-03-11 01:00:14 +04:00
int i ;
2012-06-12 21:50:43 +04:00
bool load_module = false ;
2012-03-05 23:24:50 +04:00
fw - > ucode_capa . max_probe_length = 200 ;
fw - > ucode_capa . standard_phy_calibration_size =
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE ;
if ( ! api_ok )
api_ok = api_max ;
memset ( & pieces , 0 , sizeof ( pieces ) ) ;
if ( ! ucode_raw ) {
2012-03-07 01:30:38 +04:00
if ( drv - > fw_index < = api_ok )
IWL_ERR ( drv ,
2012-03-05 23:24:50 +04:00
" request for firmware file '%s' failed. \n " ,
2012-03-07 01:30:38 +04:00
drv - > firmware_name ) ;
2012-03-05 23:24:50 +04:00
goto try_again ;
}
2012-03-07 01:30:38 +04:00
IWL_DEBUG_INFO ( drv , " Loaded firmware file '%s' (%zd bytes). \n " ,
drv - > firmware_name , ucode_raw - > size ) ;
2012-03-05 23:24:50 +04:00
/* Make sure that we got at least the API version number */
if ( ucode_raw - > size < 4 ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " File size way too small! \n " ) ;
2012-03-05 23:24:50 +04:00
goto try_again ;
}
/* Data from ucode file: header followed by uCode images */
ucode = ( struct iwl_ucode_header * ) ucode_raw - > data ;
if ( ucode - > ver )
2012-03-07 01:30:38 +04:00
err = iwl_parse_v1_v2_firmware ( drv , ucode_raw , & pieces ) ;
2012-03-05 23:24:50 +04:00
else
2012-03-07 01:30:38 +04:00
err = iwl_parse_tlv_firmware ( drv , ucode_raw , & pieces ,
2012-03-05 23:24:50 +04:00
& fw - > ucode_capa ) ;
if ( err )
goto try_again ;
2012-03-07 01:30:38 +04:00
api_ver = IWL_UCODE_API ( drv - > fw . ucode_ver ) ;
2012-03-05 23:24:50 +04:00
/*
* api_ver should match the api version forming part of the
* firmware filename . . . but we don ' t check for that and only rely
* on the API version read from firmware header from here on forward
*/
/* no api version check required for experimental uCode */
2012-03-07 01:30:38 +04:00
if ( drv - > fw_index ! = UCODE_EXPERIMENTAL_INDEX ) {
2012-03-05 23:24:50 +04:00
if ( api_ver < api_min | | api_ver > api_max ) {
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv ,
2012-03-05 23:24:50 +04:00
" Driver unable to support your firmware API. "
" Driver supports v%u, firmware is v%u. \n " ,
api_max , api_ver ) ;
goto try_again ;
}
if ( api_ver < api_ok ) {
if ( api_ok ! = api_max )
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " Firmware has old API version, "
2012-03-05 23:24:50 +04:00
" expected v%u through v%u, got v%u. \n " ,
api_ok , api_max , api_ver ) ;
else
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " Firmware has old API version, "
2012-03-05 23:24:50 +04:00
" expected v%u, got v%u. \n " ,
api_max , api_ver ) ;
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " New firmware can be obtained from "
2012-03-05 23:24:50 +04:00
" http://www.intellinuxwireless.org/. \n " ) ;
}
}
2012-03-07 01:30:38 +04:00
IWL_INFO ( drv , " loaded firmware version %s " , drv - > fw . fw_version ) ;
2012-03-05 23:24:50 +04:00
2012-03-11 01:00:15 +04:00
/*
* In mvm uCode there is no difference between data and instructions
* sections .
*/
2012-03-22 19:51:44 +04:00
if ( ! fw - > mvm_fw & & validate_sec_sizes ( drv , & pieces , drv - > cfg ) )
2012-03-05 23:24:50 +04:00
goto try_again ;
/* Allocate ucode buffers for card's bus-master loading ... */
/* Runtime instructions and 2 copies of data:
* 1 ) unmodified from disk
* 2 ) backup cache for save / restore during power - downs */
2012-03-11 01:00:14 +04:00
for ( i = 0 ; i < IWL_UCODE_TYPE_MAX ; i + + )
2012-05-17 00:49:49 +04:00
if ( iwl_alloc_ucode ( drv , & pieces , i ) )
goto out_free_fw ;
2012-03-05 23:24:50 +04:00
/* Now that we can no longer fail, copy information */
/*
* The ( size - 16 ) / 12 formula is based on the information recorded
* for each event , which is of mode 1 ( including timestamp ) for all
* new microcodes that include this information .
*/
2012-03-07 01:30:37 +04:00
fw - > init_evtlog_ptr = pieces . init_evtlog_ptr ;
2012-03-05 23:24:50 +04:00
if ( pieces . init_evtlog_size )
2012-03-07 01:30:37 +04:00
fw - > init_evtlog_size = ( pieces . init_evtlog_size - 16 ) / 12 ;
2012-03-05 23:24:50 +04:00
else
2012-03-07 01:30:37 +04:00
fw - > init_evtlog_size =
2012-03-22 19:51:44 +04:00
drv - > cfg - > base_params - > max_event_log_size ;
2012-03-07 01:30:37 +04:00
fw - > init_errlog_ptr = pieces . init_errlog_ptr ;
fw - > inst_evtlog_ptr = pieces . inst_evtlog_ptr ;
2012-03-05 23:24:50 +04:00
if ( pieces . inst_evtlog_size )
2012-03-07 01:30:37 +04:00
fw - > inst_evtlog_size = ( pieces . inst_evtlog_size - 16 ) / 12 ;
2012-03-05 23:24:50 +04:00
else
2012-03-07 01:30:37 +04:00
fw - > inst_evtlog_size =
2012-03-22 19:51:44 +04:00
drv - > cfg - > base_params - > max_event_log_size ;
2012-03-07 01:30:37 +04:00
fw - > inst_errlog_ptr = pieces . inst_errlog_ptr ;
2012-03-05 23:24:50 +04:00
/*
* figure out the offset of chain noise reset and gain commands
* base on the size of standard phy calibration commands table size
*/
if ( fw - > ucode_capa . standard_phy_calibration_size >
IWL_MAX_PHY_CALIBRATE_TBL_SIZE )
fw - > ucode_capa . standard_phy_calibration_size =
IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE ;
/* We have our copies now, allow OS release its copies */
release_firmware ( ucode_raw ) ;
2012-06-06 11:42:57 +04:00
mutex_lock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
op = & iwlwifi_opmode_table [ DVM_OP_MODE ] ;
2012-03-05 23:24:50 +04:00
2012-05-17 00:54:27 +04:00
/* add this device to the list of devices using this op_mode */
list_add_tail ( & drv - > list , & op - > drv ) ;
if ( op - > ops ) {
2012-07-17 05:43:56 +04:00
drv - > op_mode = _iwl_op_mode_start ( drv , op ) ;
2012-06-06 22:40:06 +04:00
2012-06-14 22:35:26 +04:00
if ( ! drv - > op_mode ) {
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-06-06 22:40:06 +04:00
goto out_unbind ;
2012-06-14 22:35:26 +04:00
}
2012-05-17 00:54:27 +04:00
} else {
2012-06-12 21:50:43 +04:00
load_module = true ;
2012-05-17 00:54:27 +04:00
}
2012-06-06 11:42:57 +04:00
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-03-05 23:24:50 +04:00
2012-06-05 21:56:06 +04:00
/*
* Complete the firmware request last so that
* a driver unbind ( stop ) doesn ' t run while we
* are doing the start ( ) above .
*/
complete ( & drv - > request_firmware_complete ) ;
2012-06-12 21:50:43 +04:00
/*
* Load the module last so we don ' t block anything
* else from proceeding if the module fails to load
* or hangs loading .
*/
if ( load_module )
request_module ( " %s " , op - > name ) ;
2012-03-05 23:24:50 +04:00
return ;
try_again :
/* try next, if any */
release_firmware ( ucode_raw ) ;
2012-03-07 01:30:38 +04:00
if ( iwl_request_firmware ( drv , false ) )
2012-03-05 23:24:50 +04:00
goto out_unbind ;
return ;
2012-05-17 00:49:49 +04:00
out_free_fw :
2012-03-07 01:30:38 +04:00
IWL_ERR ( drv , " failed to allocate pci memory \n " ) ;
iwl_dealloc_ucode ( drv ) ;
2012-03-05 23:24:50 +04:00
release_firmware ( ucode_raw ) ;
out_unbind :
2012-03-07 01:30:38 +04:00
complete ( & drv - > request_firmware_complete ) ;
2012-03-22 19:51:44 +04:00
device_release_driver ( drv - > trans - > dev ) ;
2012-03-05 23:24:50 +04:00
}
2012-03-26 20:03:18 +04:00
struct iwl_drv * iwl_drv_start ( struct iwl_trans * trans ,
2012-03-26 19:51:09 +04:00
const struct iwl_cfg * cfg )
2012-02-07 16:18:40 +04:00
{
2012-03-07 01:30:38 +04:00
struct iwl_drv * drv ;
2012-02-07 16:18:40 +04:00
int ret ;
2012-03-07 01:30:38 +04:00
drv = kzalloc ( sizeof ( * drv ) , GFP_KERNEL ) ;
2012-04-19 11:29:58 +04:00
if ( ! drv )
2012-03-26 19:51:09 +04:00
return NULL ;
2012-04-19 11:29:58 +04:00
2012-03-26 19:51:09 +04:00
drv - > trans = trans ;
2012-03-23 01:59:52 +04:00
drv - > dev = trans - > dev ;
2012-03-22 19:51:44 +04:00
drv - > cfg = cfg ;
2012-02-07 16:18:40 +04:00
2012-03-07 01:30:38 +04:00
init_completion ( & drv - > request_firmware_complete ) ;
2012-06-06 11:42:57 +04:00
INIT_LIST_HEAD ( & drv - > list ) ;
2012-02-07 16:18:40 +04:00
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
/* Create the device debugfs entries. */
drv - > dbgfs_drv = debugfs_create_dir ( dev_name ( trans - > dev ) ,
iwl_dbgfs_root ) ;
if ( ! drv - > dbgfs_drv ) {
IWL_ERR ( drv , " failed to create debugfs directory \n " ) ;
2012-11-28 18:42:09 +04:00
ret = - ENOMEM ;
2012-07-17 05:43:56 +04:00
goto err_free_drv ;
}
/* Create transport layer debugfs dir */
drv - > trans - > dbgfs_dir = debugfs_create_dir ( " trans " , drv - > dbgfs_drv ) ;
if ( ! drv - > trans - > dbgfs_dir ) {
IWL_ERR ( drv , " failed to create transport debugfs directory \n " ) ;
2012-11-28 18:42:09 +04:00
ret = - ENOMEM ;
2012-07-17 05:43:56 +04:00
goto err_free_dbgfs ;
}
# endif
2012-03-07 01:30:38 +04:00
ret = iwl_request_firmware ( drv , true ) ;
2012-02-07 16:18:40 +04:00
if ( ret ) {
2012-04-19 11:29:58 +04:00
IWL_ERR ( trans , " Couldn't request the fw \n " ) ;
2012-07-17 05:43:56 +04:00
goto err_fw ;
2012-02-07 16:18:40 +04:00
}
2012-07-17 05:43:56 +04:00
return drv ;
err_fw :
# ifdef CONFIG_IWLWIFI_DEBUGFS
err_free_dbgfs :
debugfs_remove_recursive ( drv - > dbgfs_drv ) ;
err_free_drv :
# endif
kfree ( drv ) ;
2012-11-28 18:42:09 +04:00
return ERR_PTR ( ret ) ;
2012-02-07 16:18:40 +04:00
}
2012-03-26 19:51:09 +04:00
void iwl_drv_stop ( struct iwl_drv * drv )
2012-02-07 16:27:31 +04:00
{
2012-03-07 01:30:38 +04:00
wait_for_completion ( & drv - > request_firmware_complete ) ;
2012-03-05 23:24:51 +04:00
2012-07-17 05:43:56 +04:00
_iwl_op_mode_stop ( drv ) ;
2012-02-07 16:27:31 +04:00
2012-03-07 01:30:38 +04:00
iwl_dealloc_ucode ( drv ) ;
2012-03-05 23:24:48 +04:00
2012-06-06 11:42:57 +04:00
mutex_lock ( & iwlwifi_opmode_table_mtx ) ;
/*
* List is empty ( this item wasn ' t added )
* when firmware loading failed - - in that
* case we can ' t remove it from any list .
*/
if ( ! list_empty ( & drv - > list ) )
list_del ( & drv - > list ) ;
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
debugfs_remove_recursive ( drv - > dbgfs_drv ) ;
# endif
2012-03-07 01:30:38 +04:00
kfree ( drv ) ;
2012-02-07 16:27:31 +04:00
}
2012-04-17 18:36:30 +04:00
/* shared module parameters */
struct iwl_mod_params iwlwifi_mod_params = {
. amsdu_size_8K = 1 ,
. restart_fw = 1 ,
. plcp_check = true ,
. bt_coex_active = true ,
. power_level = IWL_POWER_INDEX_1 ,
. bt_ch_announce = true ,
. auto_agg = true ,
2012-06-24 16:51:59 +04:00
. wd_disable = true ,
2012-04-17 18:36:30 +04:00
/* the rest are 0 by default */
} ;
2012-05-17 00:54:27 +04:00
EXPORT_SYMBOL_GPL ( iwlwifi_mod_params ) ;
int iwl_opmode_register ( const char * name , const struct iwl_op_mode_ops * ops )
{
int i ;
struct iwl_drv * drv ;
2012-07-17 05:43:56 +04:00
struct iwlwifi_opmode_table * op ;
2012-05-17 00:54:27 +04:00
2012-06-06 11:42:57 +04:00
mutex_lock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( iwlwifi_opmode_table ) ; i + + ) {
2012-07-17 05:43:56 +04:00
op = & iwlwifi_opmode_table [ i ] ;
if ( strcmp ( op - > name , name ) )
2012-05-17 00:54:27 +04:00
continue ;
2012-07-17 05:43:56 +04:00
op - > ops = ops ;
/* TODO: need to handle exceptional case */
list_for_each_entry ( drv , & op - > drv , list )
drv - > op_mode = _iwl_op_mode_start ( drv , op ) ;
2012-06-06 11:42:57 +04:00
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
return 0 ;
}
2012-06-06 11:42:57 +04:00
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
return - EIO ;
}
EXPORT_SYMBOL_GPL ( iwl_opmode_register ) ;
void iwl_opmode_deregister ( const char * name )
{
int i ;
struct iwl_drv * drv ;
2012-06-06 11:42:57 +04:00
mutex_lock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( iwlwifi_opmode_table ) ; i + + ) {
if ( strcmp ( iwlwifi_opmode_table [ i ] . name , name ) )
continue ;
iwlwifi_opmode_table [ i ] . ops = NULL ;
/* call the stop routine for all devices */
2012-07-17 05:43:56 +04:00
list_for_each_entry ( drv , & iwlwifi_opmode_table [ i ] . drv , list )
_iwl_op_mode_stop ( drv ) ;
2012-06-06 11:42:57 +04:00
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
return ;
}
2012-06-06 11:42:57 +04:00
mutex_unlock ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
}
EXPORT_SYMBOL_GPL ( iwl_opmode_deregister ) ;
static int __init iwl_drv_init ( void )
{
int i ;
2012-06-06 11:42:57 +04:00
mutex_init ( & iwlwifi_opmode_table_mtx ) ;
2012-05-17 00:54:27 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( iwlwifi_opmode_table ) ; i + + )
INIT_LIST_HEAD ( & iwlwifi_opmode_table [ i ] . drv ) ;
pr_info ( DRV_DESCRIPTION " , " DRV_VERSION " \n " ) ;
pr_info ( DRV_COPYRIGHT " \n " ) ;
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
/* Create the root of iwlwifi debugfs subsystem. */
iwl_dbgfs_root = debugfs_create_dir ( DRV_NAME , NULL ) ;
if ( ! iwl_dbgfs_root )
return - EFAULT ;
# endif
2012-05-17 00:54:27 +04:00
return iwl_pci_register_driver ( ) ;
}
module_init ( iwl_drv_init ) ;
static void __exit iwl_drv_exit ( void )
{
iwl_pci_unregister_driver ( ) ;
2012-07-17 05:43:56 +04:00
# ifdef CONFIG_IWLWIFI_DEBUGFS
debugfs_remove_recursive ( iwl_dbgfs_root ) ;
# endif
2012-05-17 00:54:27 +04:00
}
module_exit ( iwl_drv_exit ) ;
2012-04-17 18:36:30 +04:00
# ifdef CONFIG_IWLWIFI_DEBUG
module_param_named ( debug , iwlwifi_mod_params . debug_level , uint ,
S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " debug output mask " ) ;
# endif
module_param_named ( swcrypto , iwlwifi_mod_params . sw_crypto , int , S_IRUGO ) ;
MODULE_PARM_DESC ( swcrypto , " using crypto in software (default 0 [hardware]) " ) ;
module_param_named ( 11 n_disable , iwlwifi_mod_params . disable_11n , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( 11 n_disable ,
" disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX " ) ;
module_param_named ( amsdu_size_8K , iwlwifi_mod_params . amsdu_size_8K ,
int , S_IRUGO ) ;
MODULE_PARM_DESC ( amsdu_size_8K , " enable 8K amsdu size " ) ;
module_param_named ( fw_restart , iwlwifi_mod_params . restart_fw , int , S_IRUGO ) ;
MODULE_PARM_DESC ( fw_restart , " restart firmware in case of error " ) ;
module_param_named ( antenna_coupling , iwlwifi_mod_params . ant_coupling ,
int , S_IRUGO ) ;
MODULE_PARM_DESC ( antenna_coupling ,
" specify antenna coupling in dB (defualt: 0 dB) " ) ;
module_param_named ( bt_ch_inhibition , iwlwifi_mod_params . bt_ch_announce ,
bool , S_IRUGO ) ;
MODULE_PARM_DESC ( bt_ch_inhibition ,
" Enable BT channel inhibition (default: enable) " ) ;
module_param_named ( plcp_check , iwlwifi_mod_params . plcp_check , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( plcp_check , " Check plcp health (default: 1 [enabled]) " ) ;
module_param_named ( wd_disable , iwlwifi_mod_params . wd_disable , int , S_IRUGO ) ;
MODULE_PARM_DESC ( wd_disable ,
" Disable stuck queue watchdog timer 0=system default, "
" 1=disable, 2=enable (default: 0) " ) ;
/*
* set bt_coex_active to true , uCode will do kill / defer
* every time the priority line is asserted ( BT is sending signals on the
* priority line in the PCIx ) .
* set bt_coex_active to false , uCode will ignore the BT activity and
* perform the normal operation
*
* User might experience transmit issue on some platform due to WiFi / BT
* co - exist problem . The possible behaviors are :
* Able to scan and finding all the available AP
* Not able to associate with any AP
* On those platforms , WiFi communication can be restored by set
* " bt_coex_active " module parameter to " false "
*
* default : bt_coex_active = true ( BT_COEX_ENABLE )
*/
module_param_named ( bt_coex_active , iwlwifi_mod_params . bt_coex_active ,
bool , S_IRUGO ) ;
MODULE_PARM_DESC ( bt_coex_active , " enable wifi/bt co-exist (default: enable) " ) ;
module_param_named ( led_mode , iwlwifi_mod_params . led_mode , int , S_IRUGO ) ;
MODULE_PARM_DESC ( led_mode , " 0=system default, "
" 1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0) " ) ;
module_param_named ( power_save , iwlwifi_mod_params . power_save ,
bool , S_IRUGO ) ;
MODULE_PARM_DESC ( power_save ,
" enable WiFi power management (default: disable) " ) ;
module_param_named ( power_level , iwlwifi_mod_params . power_level ,
int , S_IRUGO ) ;
MODULE_PARM_DESC ( power_level ,
" default power save level (range from 1 - 5, default: 1) " ) ;
module_param_named ( auto_agg , iwlwifi_mod_params . auto_agg ,
bool , S_IRUGO ) ;
MODULE_PARM_DESC ( auto_agg ,
" enable agg w/o check traffic load (default: enable) " ) ;
2012-04-19 20:51:24 +04:00
module_param_named ( 5 ghz_disable , iwlwifi_mod_params . disable_5ghz ,
bool , S_IRUGO ) ;
MODULE_PARM_DESC ( 5 ghz_disable , " disable 5GHz band (default: 0 [enabled]) " ) ;