2013-08-23 18:35:45 +04:00
/*
* linux / drivers / scsi / esas2r / esas2r_ioctl . c
* For use with ATTO ExpressSAS R6xx SAS / SATA RAID controllers
*
* Copyright ( c ) 2001 - 2013 ATTO Technology , Inc .
* ( mailto : linuxdrivers @ attotech . com )
*
* 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 .
*
* NO WARRANTY
* THE PROGRAM IS PROVIDED ON AN " AS IS " BASIS , WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND , EITHER EXPRESS OR IMPLIED INCLUDING , WITHOUT
* LIMITATION , ANY WARRANTIES OR CONDITIONS OF TITLE , NON - INFRINGEMENT ,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE . Each Recipient is
* solely responsible for determining the appropriateness of using and
* distributing the Program and assumes all risks associated with its
* exercise of rights under this Agreement , including but not limited to
* the risks and costs of program errors , damage to or loss of data ,
* programs or equipment , and unavailability or interruption of operations .
*
* DISCLAIMER OF LIABILITY
* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING WITHOUT LIMITATION LOST PROFITS ) , 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
* HEREUNDER , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
*
* 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 - 1301 ,
* USA .
*/
2023-09-13 15:27:45 +03:00
# include <linux/bitfield.h>
2013-08-23 18:35:45 +04:00
# include "esas2r.h"
/*
* Buffered ioctl handlers . A buffered ioctl is one which requires that we
* allocate a DMA - able memory area to communicate with the firmware . In
* order to prevent continually allocating and freeing consistent memory ,
* we will allocate a global buffer the first time we need it and re - use
* it for subsequent ioctl calls that require it .
*/
u8 * esas2r_buffered_ioctl ;
dma_addr_t esas2r_buffered_ioctl_addr ;
u32 esas2r_buffered_ioctl_size ;
struct pci_dev * esas2r_buffered_ioctl_pcid ;
2023-03-29 13:14:42 +03:00
static DEFINE_SEMAPHORE ( buffered_ioctl_semaphore , 1 ) ;
2013-08-23 18:35:45 +04:00
typedef int ( * BUFFERED_IOCTL_CALLBACK ) ( struct esas2r_adapter * ,
struct esas2r_request * ,
struct esas2r_sg_context * ,
void * ) ;
typedef void ( * BUFFERED_IOCTL_DONE_CALLBACK ) ( struct esas2r_adapter * ,
struct esas2r_request * , void * ) ;
struct esas2r_buffered_ioctl {
struct esas2r_adapter * a ;
void * ioctl ;
u32 length ;
u32 control_code ;
u32 offset ;
BUFFERED_IOCTL_CALLBACK
callback ;
void * context ;
BUFFERED_IOCTL_DONE_CALLBACK
done_callback ;
void * done_context ;
} ;
static void complete_fm_api_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
a - > fm_api_command_done = 1 ;
wake_up_interruptible ( & a - > fm_api_waiter ) ;
}
/* Callbacks for building scatter/gather lists for FM API requests */
static u32 get_physaddr_fm_api ( struct esas2r_sg_context * sgc , u64 * addr )
{
struct esas2r_adapter * a = ( struct esas2r_adapter * ) sgc - > adapter ;
int offset = sgc - > cur_offset - a - > save_offset ;
( * addr ) = a - > firmware . phys + offset ;
return a - > firmware . orig_len - offset ;
}
static u32 get_physaddr_fm_api_header ( struct esas2r_sg_context * sgc , u64 * addr )
{
struct esas2r_adapter * a = ( struct esas2r_adapter * ) sgc - > adapter ;
int offset = sgc - > cur_offset - a - > save_offset ;
( * addr ) = a - > firmware . header_buff_phys + offset ;
return sizeof ( struct esas2r_flash_img ) - offset ;
}
/* Handle EXPRESS_IOCTL_RW_FIRMWARE ioctl with img_type = FW_IMG_FM_API. */
static void do_fm_api ( struct esas2r_adapter * a , struct esas2r_flash_img * fi )
{
struct esas2r_request * rq ;
2017-06-08 13:07:30 +03:00
if ( mutex_lock_interruptible ( & a - > fm_api_mutex ) ) {
2013-08-23 18:35:45 +04:00
fi - > status = FI_STAT_BUSY ;
return ;
}
rq = esas2r_alloc_request ( a ) ;
if ( rq = = NULL ) {
fi - > status = FI_STAT_BUSY ;
2014-11-16 16:35:32 +03:00
goto free_sem ;
2013-08-23 18:35:45 +04:00
}
if ( fi = = & a - > firmware . header ) {
a - > firmware . header_buff = dma_alloc_coherent ( & a - > pcid - > dev ,
( size_t ) sizeof (
struct
esas2r_flash_img ) ,
( dma_addr_t * ) & a - >
firmware .
header_buff_phys ,
GFP_KERNEL ) ;
if ( a - > firmware . header_buff = = NULL ) {
esas2r_debug ( " failed to allocate header buffer! " ) ;
fi - > status = FI_STAT_BUSY ;
2014-11-16 16:35:32 +03:00
goto free_req ;
2013-08-23 18:35:45 +04:00
}
memcpy ( a - > firmware . header_buff , fi ,
sizeof ( struct esas2r_flash_img ) ) ;
a - > save_offset = a - > firmware . header_buff ;
a - > fm_api_sgc . get_phys_addr =
( PGETPHYSADDR ) get_physaddr_fm_api_header ;
} else {
a - > save_offset = ( u8 * ) fi ;
a - > fm_api_sgc . get_phys_addr =
( PGETPHYSADDR ) get_physaddr_fm_api ;
}
rq - > comp_cb = complete_fm_api_req ;
a - > fm_api_command_done = 0 ;
a - > fm_api_sgc . cur_offset = a - > save_offset ;
if ( ! esas2r_fm_api ( a , ( struct esas2r_flash_img * ) a - > save_offset , rq ,
& a - > fm_api_sgc ) )
goto all_done ;
/* Now wait around for it to complete. */
while ( ! a - > fm_api_command_done )
wait_event_interruptible ( a - > fm_api_waiter ,
a - > fm_api_command_done ) ;
all_done :
if ( fi = = & a - > firmware . header ) {
memcpy ( fi , a - > firmware . header_buff ,
sizeof ( struct esas2r_flash_img ) ) ;
dma_free_coherent ( & a - > pcid - > dev ,
( size_t ) sizeof ( struct esas2r_flash_img ) ,
a - > firmware . header_buff ,
( dma_addr_t ) a - > firmware . header_buff_phys ) ;
}
2014-11-16 16:35:32 +03:00
free_req :
2013-08-23 18:35:45 +04:00
esas2r_free_request ( a , ( struct esas2r_request * ) rq ) ;
2014-11-16 16:35:32 +03:00
free_sem :
2017-06-08 13:07:30 +03:00
mutex_unlock ( & a - > fm_api_mutex ) ;
2013-08-23 18:35:45 +04:00
return ;
}
static void complete_nvr_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
a - > nvram_command_done = 1 ;
wake_up_interruptible ( & a - > nvram_waiter ) ;
}
/* Callback for building scatter/gather lists for buffered ioctls */
static u32 get_physaddr_buffered_ioctl ( struct esas2r_sg_context * sgc ,
u64 * addr )
{
int offset = ( u8 * ) sgc - > cur_offset - esas2r_buffered_ioctl ;
( * addr ) = esas2r_buffered_ioctl_addr + offset ;
return esas2r_buffered_ioctl_size - offset ;
}
static void complete_buffered_ioctl_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
a - > buffered_ioctl_done = 1 ;
wake_up_interruptible ( & a - > buffered_ioctl_waiter ) ;
}
static u8 handle_buffered_ioctl ( struct esas2r_buffered_ioctl * bi )
{
struct esas2r_adapter * a = bi - > a ;
struct esas2r_request * rq ;
struct esas2r_sg_context sgc ;
u8 result = IOCTL_SUCCESS ;
if ( down_interruptible ( & buffered_ioctl_semaphore ) )
return IOCTL_OUT_OF_RESOURCES ;
/* allocate a buffer or use the existing buffer. */
if ( esas2r_buffered_ioctl ) {
if ( esas2r_buffered_ioctl_size < bi - > length ) {
/* free the too-small buffer and get a new one */
dma_free_coherent ( & a - > pcid - > dev ,
( size_t ) esas2r_buffered_ioctl_size ,
esas2r_buffered_ioctl ,
esas2r_buffered_ioctl_addr ) ;
goto allocate_buffer ;
}
} else {
allocate_buffer :
esas2r_buffered_ioctl_size = bi - > length ;
esas2r_buffered_ioctl_pcid = a - > pcid ;
esas2r_buffered_ioctl = dma_alloc_coherent ( & a - > pcid - > dev ,
( size_t )
esas2r_buffered_ioctl_size ,
&
esas2r_buffered_ioctl_addr ,
GFP_KERNEL ) ;
}
if ( ! esas2r_buffered_ioctl ) {
esas2r_log ( ESAS2R_LOG_CRIT ,
" could not allocate %d bytes of consistent memory "
" for a buffered ioctl! " ,
bi - > length ) ;
esas2r_debug ( " buffered ioctl alloc failure " ) ;
result = IOCTL_OUT_OF_RESOURCES ;
goto exit_cleanly ;
}
memcpy ( esas2r_buffered_ioctl , bi - > ioctl , bi - > length ) ;
rq = esas2r_alloc_request ( a ) ;
if ( rq = = NULL ) {
esas2r_log ( ESAS2R_LOG_CRIT ,
" could not allocate an internal request " ) ;
result = IOCTL_OUT_OF_RESOURCES ;
esas2r_debug ( " buffered ioctl - no requests " ) ;
goto exit_cleanly ;
}
a - > buffered_ioctl_done = 0 ;
rq - > comp_cb = complete_buffered_ioctl_req ;
sgc . cur_offset = esas2r_buffered_ioctl + bi - > offset ;
sgc . get_phys_addr = ( PGETPHYSADDR ) get_physaddr_buffered_ioctl ;
sgc . length = esas2r_buffered_ioctl_size ;
if ( ! ( * bi - > callback ) ( a , rq , & sgc , bi - > context ) ) {
/* completed immediately, no need to wait */
a - > buffered_ioctl_done = 0 ;
goto free_andexit_cleanly ;
}
/* now wait around for it to complete. */
while ( ! a - > buffered_ioctl_done )
wait_event_interruptible ( a - > buffered_ioctl_waiter ,
a - > buffered_ioctl_done ) ;
free_andexit_cleanly :
if ( result = = IOCTL_SUCCESS & & bi - > done_callback )
( * bi - > done_callback ) ( a , rq , bi - > done_context ) ;
esas2r_free_request ( a , rq ) ;
exit_cleanly :
if ( result = = IOCTL_SUCCESS )
memcpy ( bi - > ioctl , esas2r_buffered_ioctl , bi - > length ) ;
up ( & buffered_ioctl_semaphore ) ;
return result ;
}
/* SMP ioctl support */
static int smp_ioctl_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc , void * context )
{
struct atto_ioctl_smp * si =
( struct atto_ioctl_smp * ) esas2r_buffered_ioctl ;
esas2r_sgc_init ( sgc , a , rq , rq - > vrq - > ioctl . sge ) ;
esas2r_build_ioctl_req ( a , rq , sgc - > length , VDA_IOCTL_SMP ) ;
if ( ! esas2r_build_sg_list ( a , rq , sgc ) ) {
si - > status = ATTO_STS_OUT_OF_RSRC ;
return false ;
}
esas2r_start_request ( a , rq ) ;
return true ;
}
static u8 handle_smp_ioctl ( struct esas2r_adapter * a , struct atto_ioctl_smp * si )
{
struct esas2r_buffered_ioctl bi ;
memset ( & bi , 0 , sizeof ( bi ) ) ;
bi . a = a ;
bi . ioctl = si ;
bi . length = sizeof ( struct atto_ioctl_smp )
+ le32_to_cpu ( si - > req_length )
+ le32_to_cpu ( si - > rsp_length ) ;
bi . offset = 0 ;
bi . callback = smp_ioctl_callback ;
return handle_buffered_ioctl ( & bi ) ;
}
/* CSMI ioctl support */
static void esas2r_csmi_ioctl_tunnel_comp_cb ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
rq - > target_id = le16_to_cpu ( rq - > func_rsp . ioctl_rsp . csmi . target_id ) ;
rq - > vrq - > scsi . flags | = cpu_to_le32 ( rq - > func_rsp . ioctl_rsp . csmi . lun ) ;
/* Now call the original completion callback. */
( * rq - > aux_req_cb ) ( a , rq ) ;
}
/* Tunnel a CSMI IOCTL to the back end driver for processing. */
static bool csmi_ioctl_tunnel ( struct esas2r_adapter * a ,
union atto_ioctl_csmi * ci ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc ,
u32 ctrl_code ,
u16 target_id )
{
struct atto_vda_ioctl_req * ioctl = & rq - > vrq - > ioctl ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
return false ;
esas2r_sgc_init ( sgc , a , rq , rq - > vrq - > ioctl . sge ) ;
esas2r_build_ioctl_req ( a , rq , sgc - > length , VDA_IOCTL_CSMI ) ;
ioctl - > csmi . ctrl_code = cpu_to_le32 ( ctrl_code ) ;
ioctl - > csmi . target_id = cpu_to_le16 ( target_id ) ;
ioctl - > csmi . lun = ( u8 ) le32_to_cpu ( rq - > vrq - > scsi . flags ) ;
/*
* Always usurp the completion callback since the interrupt callback
* mechanism may be used .
*/
rq - > aux_req_cx = ci ;
rq - > aux_req_cb = rq - > comp_cb ;
rq - > comp_cb = esas2r_csmi_ioctl_tunnel_comp_cb ;
if ( ! esas2r_build_sg_list ( a , rq , sgc ) )
return false ;
esas2r_start_request ( a , rq ) ;
return true ;
}
static bool check_lun ( struct scsi_lun lun )
{
bool result ;
result = ( ( lun . scsi_lun [ 7 ] = = 0 ) & &
( lun . scsi_lun [ 6 ] = = 0 ) & &
( lun . scsi_lun [ 5 ] = = 0 ) & &
( lun . scsi_lun [ 4 ] = = 0 ) & &
( lun . scsi_lun [ 3 ] = = 0 ) & &
( lun . scsi_lun [ 2 ] = = 0 ) & &
/* Byte 1 is intentionally skipped */
( lun . scsi_lun [ 0 ] = = 0 ) ) ;
return result ;
}
static int csmi_ioctl_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc , void * context )
{
struct atto_csmi * ci = ( struct atto_csmi * ) context ;
union atto_ioctl_csmi * ioctl_csmi =
( union atto_ioctl_csmi * ) esas2r_buffered_ioctl ;
u8 path = 0 ;
u8 tid = 0 ;
u8 lun = 0 ;
u32 sts = CSMI_STS_SUCCESS ;
struct esas2r_target * t ;
unsigned long flags ;
if ( ci - > control_code = = CSMI_CC_GET_DEV_ADDR ) {
struct atto_csmi_get_dev_addr * gda = & ci - > data . dev_addr ;
path = gda - > path_id ;
tid = gda - > target_id ;
lun = gda - > lun ;
} else if ( ci - > control_code = = CSMI_CC_TASK_MGT ) {
struct atto_csmi_task_mgmt * tm = & ci - > data . tsk_mgt ;
path = tm - > path_id ;
tid = tm - > target_id ;
lun = tm - > lun ;
}
2013-08-29 23:55:43 +04:00
if ( path > 0 ) {
2013-08-23 18:35:45 +04:00
rq - > func_rsp . ioctl_rsp . csmi . csmi_status = cpu_to_le32 (
CSMI_STS_INV_PARAM ) ;
return false ;
}
rq - > target_id = tid ;
rq - > vrq - > scsi . flags | = cpu_to_le32 ( lun ) ;
switch ( ci - > control_code ) {
case CSMI_CC_GET_DRVR_INFO :
{
struct atto_csmi_get_driver_info * gdi = & ioctl_csmi - > drvr_info ;
strcpy ( gdi - > description , esas2r_get_model_name ( a ) ) ;
gdi - > csmi_major_rev = CSMI_MAJOR_REV ;
gdi - > csmi_minor_rev = CSMI_MINOR_REV ;
break ;
}
case CSMI_CC_GET_CNTLR_CFG :
{
struct atto_csmi_get_cntlr_cfg * gcc = & ioctl_csmi - > cntlr_cfg ;
gcc - > base_io_addr = 0 ;
pci_read_config_dword ( a - > pcid , PCI_BASE_ADDRESS_2 ,
& gcc - > base_memaddr_lo ) ;
pci_read_config_dword ( a - > pcid , PCI_BASE_ADDRESS_3 ,
& gcc - > base_memaddr_hi ) ;
gcc - > board_id = MAKEDWORD ( a - > pcid - > subsystem_device ,
a - > pcid - > subsystem_vendor ) ;
gcc - > slot_num = CSMI_SLOT_NUM_UNKNOWN ;
gcc - > cntlr_class = CSMI_CNTLR_CLASS_HBA ;
gcc - > io_bus_type = CSMI_BUS_TYPE_PCI ;
gcc - > pci_addr . bus_num = a - > pcid - > bus - > number ;
gcc - > pci_addr . device_num = PCI_SLOT ( a - > pcid - > devfn ) ;
gcc - > pci_addr . function_num = PCI_FUNC ( a - > pcid - > devfn ) ;
memset ( gcc - > serial_num , 0 , sizeof ( gcc - > serial_num ) ) ;
gcc - > major_rev = LOBYTE ( LOWORD ( a - > fw_version ) ) ;
gcc - > minor_rev = HIBYTE ( LOWORD ( a - > fw_version ) ) ;
gcc - > build_rev = LOBYTE ( HIWORD ( a - > fw_version ) ) ;
gcc - > release_rev = HIBYTE ( HIWORD ( a - > fw_version ) ) ;
gcc - > bios_major_rev = HIBYTE ( HIWORD ( a - > flash_ver ) ) ;
gcc - > bios_minor_rev = LOBYTE ( HIWORD ( a - > flash_ver ) ) ;
gcc - > bios_build_rev = LOWORD ( a - > flash_ver ) ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF2_THUNDERLINK , & a - > flags2 ) )
2013-08-23 18:35:45 +04:00
gcc - > cntlr_flags = CSMI_CNTLRF_SAS_HBA
| CSMI_CNTLRF_SATA_HBA ;
else
gcc - > cntlr_flags = CSMI_CNTLRF_SAS_RAID
| CSMI_CNTLRF_SATA_RAID ;
gcc - > rrom_major_rev = 0 ;
gcc - > rrom_minor_rev = 0 ;
gcc - > rrom_build_rev = 0 ;
gcc - > rrom_release_rev = 0 ;
gcc - > rrom_biosmajor_rev = 0 ;
gcc - > rrom_biosminor_rev = 0 ;
gcc - > rrom_biosbuild_rev = 0 ;
gcc - > rrom_biosrelease_rev = 0 ;
break ;
}
case CSMI_CC_GET_CNTLR_STS :
{
struct atto_csmi_get_cntlr_sts * gcs = & ioctl_csmi - > cntlr_sts ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
gcs - > status = CSMI_CNTLR_STS_FAILED ;
else
gcs - > status = CSMI_CNTLR_STS_GOOD ;
gcs - > offline_reason = CSMI_OFFLINE_NO_REASON ;
break ;
}
case CSMI_CC_FW_DOWNLOAD :
case CSMI_CC_GET_RAID_INFO :
case CSMI_CC_GET_RAID_CFG :
sts = CSMI_STS_BAD_CTRL_CODE ;
break ;
case CSMI_CC_SMP_PASSTHRU :
case CSMI_CC_SSP_PASSTHRU :
case CSMI_CC_STP_PASSTHRU :
case CSMI_CC_GET_PHY_INFO :
case CSMI_CC_SET_PHY_INFO :
case CSMI_CC_GET_LINK_ERRORS :
case CSMI_CC_GET_SATA_SIG :
case CSMI_CC_GET_CONN_INFO :
case CSMI_CC_PHY_CTRL :
if ( ! csmi_ioctl_tunnel ( a , ioctl_csmi , rq , sgc ,
ci - > control_code ,
ESAS2R_TARG_ID_INV ) ) {
sts = CSMI_STS_FAILED ;
break ;
}
return true ;
case CSMI_CC_GET_SCSI_ADDR :
{
struct atto_csmi_get_scsi_addr * gsa = & ioctl_csmi - > scsi_addr ;
struct scsi_lun lun ;
memcpy ( & lun , gsa - > sas_lun , sizeof ( struct scsi_lun ) ) ;
if ( ! check_lun ( lun ) ) {
sts = CSMI_STS_NO_SCSI_ADDR ;
break ;
}
/* make sure the device is present */
spin_lock_irqsave ( & a - > mem_lock , flags ) ;
t = esas2r_targ_db_find_by_sas_addr ( a , ( u64 * ) gsa - > sas_addr ) ;
spin_unlock_irqrestore ( & a - > mem_lock , flags ) ;
if ( t = = NULL ) {
sts = CSMI_STS_NO_SCSI_ADDR ;
break ;
}
gsa - > host_index = 0xFF ;
gsa - > lun = gsa - > sas_lun [ 1 ] ;
rq - > target_id = esas2r_targ_get_id ( t , a ) ;
break ;
}
case CSMI_CC_GET_DEV_ADDR :
{
struct atto_csmi_get_dev_addr * gda = & ioctl_csmi - > dev_addr ;
/* make sure the target is present */
t = a - > targetdb + rq - > target_id ;
if ( t > = a - > targetdb_end
| | t - > target_state ! = TS_PRESENT
| | t - > sas_addr = = 0 ) {
sts = CSMI_STS_NO_DEV_ADDR ;
break ;
}
/* fill in the result */
* ( u64 * ) gda - > sas_addr = t - > sas_addr ;
memset ( gda - > sas_lun , 0 , sizeof ( gda - > sas_lun ) ) ;
gda - > sas_lun [ 1 ] = ( u8 ) le32_to_cpu ( rq - > vrq - > scsi . flags ) ;
break ;
}
case CSMI_CC_TASK_MGT :
/* make sure the target is present */
t = a - > targetdb + rq - > target_id ;
if ( t > = a - > targetdb_end
| | t - > target_state ! = TS_PRESENT
| | ! ( t - > flags & TF_PASS_THRU ) ) {
sts = CSMI_STS_NO_DEV_ADDR ;
break ;
}
if ( ! csmi_ioctl_tunnel ( a , ioctl_csmi , rq , sgc ,
ci - > control_code ,
t - > phys_targ_id ) ) {
sts = CSMI_STS_FAILED ;
break ;
}
return true ;
default :
sts = CSMI_STS_BAD_CTRL_CODE ;
break ;
}
rq - > func_rsp . ioctl_rsp . csmi . csmi_status = cpu_to_le32 ( sts ) ;
return false ;
}
static void csmi_ioctl_done_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq , void * context )
{
struct atto_csmi * ci = ( struct atto_csmi * ) context ;
union atto_ioctl_csmi * ioctl_csmi =
( union atto_ioctl_csmi * ) esas2r_buffered_ioctl ;
switch ( ci - > control_code ) {
case CSMI_CC_GET_DRVR_INFO :
{
struct atto_csmi_get_driver_info * gdi =
& ioctl_csmi - > drvr_info ;
strcpy ( gdi - > name , ESAS2R_VERSION_STR ) ;
gdi - > major_rev = ESAS2R_MAJOR_REV ;
gdi - > minor_rev = ESAS2R_MINOR_REV ;
gdi - > build_rev = 0 ;
gdi - > release_rev = 0 ;
break ;
}
case CSMI_CC_GET_SCSI_ADDR :
{
struct atto_csmi_get_scsi_addr * gsa = & ioctl_csmi - > scsi_addr ;
if ( le32_to_cpu ( rq - > func_rsp . ioctl_rsp . csmi . csmi_status ) = =
CSMI_STS_SUCCESS ) {
gsa - > target_id = rq - > target_id ;
gsa - > path_id = 0 ;
}
break ;
}
}
ci - > status = le32_to_cpu ( rq - > func_rsp . ioctl_rsp . csmi . csmi_status ) ;
}
static u8 handle_csmi_ioctl ( struct esas2r_adapter * a , struct atto_csmi * ci )
{
struct esas2r_buffered_ioctl bi ;
memset ( & bi , 0 , sizeof ( bi ) ) ;
bi . a = a ;
bi . ioctl = & ci - > data ;
bi . length = sizeof ( union atto_ioctl_csmi ) ;
bi . offset = 0 ;
bi . callback = csmi_ioctl_callback ;
bi . context = ci ;
bi . done_callback = csmi_ioctl_done_callback ;
bi . done_context = ci ;
return handle_buffered_ioctl ( & bi ) ;
}
/* ATTO HBA ioctl support */
/* Tunnel an ATTO HBA IOCTL to the back end driver for processing. */
static bool hba_ioctl_tunnel ( struct esas2r_adapter * a ,
struct atto_ioctl * hi ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc )
{
esas2r_sgc_init ( sgc , a , rq , rq - > vrq - > ioctl . sge ) ;
esas2r_build_ioctl_req ( a , rq , sgc - > length , VDA_IOCTL_HBA ) ;
if ( ! esas2r_build_sg_list ( a , rq , sgc ) ) {
hi - > status = ATTO_STS_OUT_OF_RSRC ;
return false ;
}
esas2r_start_request ( a , rq ) ;
return true ;
}
static void scsi_passthru_comp_cb ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct atto_ioctl * hi = ( struct atto_ioctl * ) rq - > aux_req_cx ;
struct atto_hba_scsi_pass_thru * spt = & hi - > data . scsi_pass_thru ;
u8 sts = ATTO_SPT_RS_FAILED ;
spt - > scsi_status = rq - > func_rsp . scsi_rsp . scsi_stat ;
spt - > sense_length = rq - > sense_len ;
spt - > residual_length =
le32_to_cpu ( rq - > func_rsp . scsi_rsp . residual_length ) ;
switch ( rq - > req_stat ) {
case RS_SUCCESS :
case RS_SCSI_ERROR :
sts = ATTO_SPT_RS_SUCCESS ;
break ;
case RS_UNDERRUN :
sts = ATTO_SPT_RS_UNDERRUN ;
break ;
case RS_OVERRUN :
sts = ATTO_SPT_RS_OVERRUN ;
break ;
case RS_SEL :
case RS_SEL2 :
sts = ATTO_SPT_RS_NO_DEVICE ;
break ;
case RS_NO_LUN :
sts = ATTO_SPT_RS_NO_LUN ;
break ;
case RS_TIMEOUT :
sts = ATTO_SPT_RS_TIMEOUT ;
break ;
case RS_DEGRADED :
sts = ATTO_SPT_RS_DEGRADED ;
break ;
case RS_BUSY :
sts = ATTO_SPT_RS_BUSY ;
break ;
case RS_ABORTED :
sts = ATTO_SPT_RS_ABORTED ;
break ;
case RS_RESET :
sts = ATTO_SPT_RS_BUS_RESET ;
break ;
}
spt - > req_status = sts ;
/* Update the target ID to the next one present. */
spt - > target_id =
esas2r_targ_db_find_next_present ( a , ( u16 ) spt - > target_id ) ;
/* Done, call the completion callback. */
( * rq - > aux_req_cb ) ( a , rq ) ;
}
static int hba_ioctl_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc ,
void * context )
{
struct atto_ioctl * hi = ( struct atto_ioctl * ) esas2r_buffered_ioctl ;
hi - > status = ATTO_STS_SUCCESS ;
switch ( hi - > function ) {
case ATTO_FUNC_GET_ADAP_INFO :
{
u8 * class_code = ( u8 * ) & a - > pcid - > class ;
struct atto_hba_get_adapter_info * gai =
& hi - > data . get_adap_info ;
if ( hi - > flags & HBAF_TUNNEL ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
if ( hi - > version > ATTO_VER_GET_ADAP_INFO0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_GET_ADAP_INFO0 ;
break ;
}
memset ( gai , 0 , sizeof ( * gai ) ) ;
gai - > pci . vendor_id = a - > pcid - > vendor ;
gai - > pci . device_id = a - > pcid - > device ;
gai - > pci . ss_vendor_id = a - > pcid - > subsystem_vendor ;
gai - > pci . ss_device_id = a - > pcid - > subsystem_device ;
gai - > pci . class_code [ 0 ] = class_code [ 0 ] ;
gai - > pci . class_code [ 1 ] = class_code [ 1 ] ;
gai - > pci . class_code [ 2 ] = class_code [ 2 ] ;
gai - > pci . rev_id = a - > pcid - > revision ;
gai - > pci . bus_num = a - > pcid - > bus - > number ;
gai - > pci . dev_num = PCI_SLOT ( a - > pcid - > devfn ) ;
gai - > pci . func_num = PCI_FUNC ( a - > pcid - > devfn ) ;
2019-07-18 05:07:44 +03:00
if ( pci_is_pcie ( a - > pcid ) ) {
2013-08-23 18:35:45 +04:00
u16 stat ;
u32 caps ;
2019-07-18 05:07:44 +03:00
pcie_capability_read_word ( a - > pcid , PCI_EXP_LNKSTA ,
& stat ) ;
pcie_capability_read_dword ( a - > pcid , PCI_EXP_LNKCAP ,
& caps ) ;
2013-08-23 18:35:45 +04:00
2023-09-13 15:27:45 +03:00
gai - > pci . link_speed_curr = FIELD_GET ( PCI_EXP_LNKSTA_CLS , stat ) ;
gai - > pci . link_speed_max = FIELD_GET ( PCI_EXP_LNKCAP_SLS , caps ) ;
gai - > pci . link_width_curr = FIELD_GET ( PCI_EXP_LNKSTA_NLW , stat ) ;
gai - > pci . link_width_max = FIELD_GET ( PCI_EXP_LNKCAP_MLW , caps ) ;
2013-08-23 18:35:45 +04:00
}
gai - > pci . msi_vector_cnt = 1 ;
if ( a - > pcid - > msix_enabled )
gai - > pci . interrupt_mode = ATTO_GAI_PCIIM_MSIX ;
else if ( a - > pcid - > msi_enabled )
gai - > pci . interrupt_mode = ATTO_GAI_PCIIM_MSI ;
else
gai - > pci . interrupt_mode = ATTO_GAI_PCIIM_LEGACY ;
gai - > adap_type = ATTO_GAI_AT_ESASRAID2 ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF2_THUNDERLINK , & a - > flags2 ) )
2013-08-23 18:35:45 +04:00
gai - > adap_type = ATTO_GAI_AT_TLSASHBA ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
gai - > adap_flags | = ATTO_GAI_AF_DEGRADED ;
gai - > adap_flags | = ATTO_GAI_AF_SPT_SUPP |
ATTO_GAI_AF_DEVADDR_SUPP ;
if ( a - > pcid - > subsystem_device = = ATTO_ESAS_R60F
| | a - > pcid - > subsystem_device = = ATTO_ESAS_R608
| | a - > pcid - > subsystem_device = = ATTO_ESAS_R644
| | a - > pcid - > subsystem_device = = ATTO_TSSC_3808E )
gai - > adap_flags | = ATTO_GAI_AF_VIRT_SES ;
gai - > num_ports = ESAS2R_NUM_PHYS ;
gai - > num_phys = ESAS2R_NUM_PHYS ;
strcpy ( gai - > firmware_rev , a - > fw_rev ) ;
strcpy ( gai - > flash_rev , a - > flash_rev ) ;
strcpy ( gai - > model_name_short , esas2r_get_model_name_short ( a ) ) ;
strcpy ( gai - > model_name , esas2r_get_model_name ( a ) ) ;
gai - > num_targets = ESAS2R_MAX_TARGETS ;
gai - > num_busses = 1 ;
gai - > num_targsper_bus = gai - > num_targets ;
gai - > num_lunsper_targ = 256 ;
if ( a - > pcid - > subsystem_device = = ATTO_ESAS_R6F0
| | a - > pcid - > subsystem_device = = ATTO_ESAS_R60F )
gai - > num_connectors = 4 ;
else
gai - > num_connectors = 2 ;
gai - > adap_flags2 | = ATTO_GAI_AF2_ADAP_CTRL_SUPP ;
gai - > num_targets_backend = a - > num_targets_backend ;
gai - > tunnel_flags = a - > ioctl_tunnel
& ( ATTO_GAI_TF_MEM_RW
| ATTO_GAI_TF_TRACE
| ATTO_GAI_TF_SCSI_PASS_THRU
| ATTO_GAI_TF_GET_DEV_ADDR
| ATTO_GAI_TF_PHY_CTRL
| ATTO_GAI_TF_CONN_CTRL
| ATTO_GAI_TF_GET_DEV_INFO ) ;
break ;
}
case ATTO_FUNC_GET_ADAP_ADDR :
{
struct atto_hba_get_adapter_address * gaa =
& hi - > data . get_adap_addr ;
if ( hi - > flags & HBAF_TUNNEL ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
if ( hi - > version > ATTO_VER_GET_ADAP_ADDR0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_GET_ADAP_ADDR0 ;
} else if ( gaa - > addr_type = = ATTO_GAA_AT_PORT
| | gaa - > addr_type = = ATTO_GAA_AT_NODE ) {
if ( gaa - > addr_type = = ATTO_GAA_AT_PORT
& & gaa - > port_id > = ESAS2R_NUM_PHYS ) {
hi - > status = ATTO_STS_NOT_APPL ;
} else {
memcpy ( ( u64 * ) gaa - > address ,
& a - > nvram - > sas_addr [ 0 ] , sizeof ( u64 ) ) ;
gaa - > addr_len = sizeof ( u64 ) ;
}
} else {
hi - > status = ATTO_STS_INV_PARAM ;
}
break ;
}
case ATTO_FUNC_MEM_RW :
{
if ( hi - > flags & HBAF_TUNNEL ) {
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
case ATTO_FUNC_TRACE :
{
struct atto_hba_trace * trc = & hi - > data . trace ;
if ( hi - > flags & HBAF_TUNNEL ) {
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
if ( hi - > version > ATTO_VER_TRACE1 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_TRACE1 ;
break ;
}
if ( trc - > trace_type = = ATTO_TRC_TT_FWCOREDUMP
& & hi - > version > = ATTO_VER_TRACE1 ) {
if ( trc - > trace_func = = ATTO_TRC_TF_UPLOAD ) {
u32 len = hi - > data_length ;
u32 offset = trc - > current_offset ;
u32 total_len = ESAS2R_FWCOREDUMP_SZ ;
/* Size is zero if a core dump isn't present */
2013-10-01 22:26:01 +04:00
if ( ! test_bit ( AF2_COREDUMP_SAVED , & a - > flags2 ) )
2013-08-23 18:35:45 +04:00
total_len = 0 ;
if ( len > total_len )
len = total_len ;
if ( offset > = total_len
| | offset + len > total_len
| | len = = 0 ) {
hi - > status = ATTO_STS_INV_PARAM ;
break ;
}
2022-09-01 23:57:29 +03:00
memcpy ( trc - > contents ,
2013-08-23 18:35:45 +04:00
a - > fw_coredump_buff + offset ,
len ) ;
hi - > data_length = len ;
} else if ( trc - > trace_func = = ATTO_TRC_TF_RESET ) {
memset ( a - > fw_coredump_buff , 0 ,
ESAS2R_FWCOREDUMP_SZ ) ;
2013-10-01 22:26:01 +04:00
clear_bit ( AF2_COREDUMP_SAVED , & a - > flags2 ) ;
2013-08-23 18:35:45 +04:00
} else if ( trc - > trace_func ! = ATTO_TRC_TF_GET_INFO ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
/* Always return all the info we can. */
trc - > trace_mask = 0 ;
trc - > current_offset = 0 ;
trc - > total_length = ESAS2R_FWCOREDUMP_SZ ;
/* Return zero length buffer if core dump not present */
2013-10-01 22:26:01 +04:00
if ( ! test_bit ( AF2_COREDUMP_SAVED , & a - > flags2 ) )
2013-08-23 18:35:45 +04:00
trc - > total_length = 0 ;
} else {
hi - > status = ATTO_STS_UNSUPPORTED ;
}
break ;
}
case ATTO_FUNC_SCSI_PASS_THRU :
{
struct atto_hba_scsi_pass_thru * spt = & hi - > data . scsi_pass_thru ;
struct scsi_lun lun ;
memcpy ( & lun , spt - > lun , sizeof ( struct scsi_lun ) ) ;
if ( hi - > flags & HBAF_TUNNEL ) {
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
if ( hi - > version > ATTO_VER_SCSI_PASS_THRU0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_SCSI_PASS_THRU0 ;
break ;
}
if ( spt - > target_id > = ESAS2R_MAX_TARGETS | | ! check_lun ( lun ) ) {
hi - > status = ATTO_STS_INV_PARAM ;
break ;
}
esas2r_sgc_init ( sgc , a , rq , NULL ) ;
sgc - > length = hi - > data_length ;
sgc - > cur_offset + = offsetof ( struct atto_ioctl , data . byte )
+ sizeof ( struct atto_hba_scsi_pass_thru ) ;
/* Finish request initialization */
rq - > target_id = ( u16 ) spt - > target_id ;
rq - > vrq - > scsi . flags | = cpu_to_le32 ( spt - > lun [ 1 ] ) ;
memcpy ( rq - > vrq - > scsi . cdb , spt - > cdb , 16 ) ;
rq - > vrq - > scsi . length = cpu_to_le32 ( hi - > data_length ) ;
rq - > sense_len = spt - > sense_length ;
rq - > sense_buf = ( u8 * ) spt - > sense_data ;
/* NOTE: we ignore spt->timeout */
/*
* always usurp the completion callback since the interrupt
* callback mechanism may be used .
*/
rq - > aux_req_cx = hi ;
rq - > aux_req_cb = rq - > comp_cb ;
rq - > comp_cb = scsi_passthru_comp_cb ;
if ( spt - > flags & ATTO_SPTF_DATA_IN ) {
rq - > vrq - > scsi . flags | = cpu_to_le32 ( FCP_CMND_RDD ) ;
} else if ( spt - > flags & ATTO_SPTF_DATA_OUT ) {
rq - > vrq - > scsi . flags | = cpu_to_le32 ( FCP_CMND_WRD ) ;
} else {
if ( sgc - > length ) {
hi - > status = ATTO_STS_INV_PARAM ;
break ;
}
}
if ( spt - > flags & ATTO_SPTF_ORDERED_Q )
rq - > vrq - > scsi . flags | =
cpu_to_le32 ( FCP_CMND_TA_ORDRD_Q ) ;
else if ( spt - > flags & ATTO_SPTF_HEAD_OF_Q )
rq - > vrq - > scsi . flags | = cpu_to_le32 ( FCP_CMND_TA_HEAD_Q ) ;
2013-10-01 22:26:01 +04:00
2013-08-23 18:35:45 +04:00
if ( ! esas2r_build_sg_list ( a , rq , sgc ) ) {
hi - > status = ATTO_STS_OUT_OF_RSRC ;
break ;
}
esas2r_start_request ( a , rq ) ;
return true ;
}
case ATTO_FUNC_GET_DEV_ADDR :
{
struct atto_hba_get_device_address * gda =
& hi - > data . get_dev_addr ;
struct esas2r_target * t ;
if ( hi - > flags & HBAF_TUNNEL ) {
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
if ( hi - > version > ATTO_VER_GET_DEV_ADDR0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_GET_DEV_ADDR0 ;
break ;
}
if ( gda - > target_id > = ESAS2R_MAX_TARGETS ) {
hi - > status = ATTO_STS_INV_PARAM ;
break ;
}
t = a - > targetdb + ( u16 ) gda - > target_id ;
if ( t - > target_state ! = TS_PRESENT ) {
hi - > status = ATTO_STS_FAILED ;
} else if ( gda - > addr_type = = ATTO_GDA_AT_PORT ) {
if ( t - > sas_addr = = 0 ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
} else {
* ( u64 * ) gda - > address = t - > sas_addr ;
gda - > addr_len = sizeof ( u64 ) ;
}
} else if ( gda - > addr_type = = ATTO_GDA_AT_NODE ) {
hi - > status = ATTO_STS_NOT_APPL ;
} else {
hi - > status = ATTO_STS_INV_PARAM ;
}
/* update the target ID to the next one present. */
gda - > target_id =
esas2r_targ_db_find_next_present ( a ,
( u16 ) gda - > target_id ) ;
break ;
}
case ATTO_FUNC_PHY_CTRL :
case ATTO_FUNC_CONN_CTRL :
{
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
case ATTO_FUNC_ADAP_CTRL :
{
struct atto_hba_adap_ctrl * ac = & hi - > data . adap_ctrl ;
if ( hi - > flags & HBAF_TUNNEL ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
if ( hi - > version > ATTO_VER_ADAP_CTRL0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_ADAP_CTRL0 ;
break ;
}
if ( ac - > adap_func = = ATTO_AC_AF_HARD_RST ) {
esas2r_reset_adapter ( a ) ;
} else if ( ac - > adap_func ! = ATTO_AC_AF_GET_STATE ) {
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_CHPRST_NEEDED , & a - > flags ) )
2013-08-23 18:35:45 +04:00
ac - > adap_state = ATTO_AC_AS_RST_SCHED ;
2013-10-01 22:26:01 +04:00
else if ( test_bit ( AF_CHPRST_PENDING , & a - > flags ) )
2013-08-23 18:35:45 +04:00
ac - > adap_state = ATTO_AC_AS_RST_IN_PROG ;
2013-10-01 22:26:01 +04:00
else if ( test_bit ( AF_DISC_PENDING , & a - > flags ) )
2013-08-23 18:35:45 +04:00
ac - > adap_state = ATTO_AC_AS_RST_DISC ;
2013-10-01 22:26:01 +04:00
else if ( test_bit ( AF_DISABLED , & a - > flags ) )
2013-08-23 18:35:45 +04:00
ac - > adap_state = ATTO_AC_AS_DISABLED ;
2013-10-01 22:26:01 +04:00
else if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
ac - > adap_state = ATTO_AC_AS_DEGRADED ;
else
ac - > adap_state = ATTO_AC_AS_OK ;
break ;
}
case ATTO_FUNC_GET_DEV_INFO :
{
struct atto_hba_get_device_info * gdi = & hi - > data . get_dev_info ;
struct esas2r_target * t ;
if ( hi - > flags & HBAF_TUNNEL ) {
if ( hba_ioctl_tunnel ( a , hi , rq , sgc ) )
return true ;
break ;
}
if ( hi - > version > ATTO_VER_GET_DEV_INFO0 ) {
hi - > status = ATTO_STS_INV_VERSION ;
hi - > version = ATTO_VER_GET_DEV_INFO0 ;
break ;
}
if ( gdi - > target_id > = ESAS2R_MAX_TARGETS ) {
hi - > status = ATTO_STS_INV_PARAM ;
break ;
}
t = a - > targetdb + ( u16 ) gdi - > target_id ;
/* update the target ID to the next one present. */
gdi - > target_id =
esas2r_targ_db_find_next_present ( a ,
( u16 ) gdi - > target_id ) ;
if ( t - > target_state ! = TS_PRESENT ) {
hi - > status = ATTO_STS_FAILED ;
break ;
}
hi - > status = ATTO_STS_UNSUPPORTED ;
break ;
}
default :
hi - > status = ATTO_STS_INV_FUNC ;
break ;
}
return false ;
}
static void hba_ioctl_done_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq , void * context )
{
struct atto_ioctl * ioctl_hba =
( struct atto_ioctl * ) esas2r_buffered_ioctl ;
esas2r_debug ( " hba_ioctl_done_callback %d " , a - > index ) ;
if ( ioctl_hba - > function = = ATTO_FUNC_GET_ADAP_INFO ) {
struct atto_hba_get_adapter_info * gai =
& ioctl_hba - > data . get_adap_info ;
esas2r_debug ( " ATTO_FUNC_GET_ADAP_INFO " ) ;
gai - > drvr_rev_major = ESAS2R_MAJOR_REV ;
gai - > drvr_rev_minor = ESAS2R_MINOR_REV ;
strcpy ( gai - > drvr_rev_ascii , ESAS2R_VERSION_STR ) ;
strcpy ( gai - > drvr_name , ESAS2R_DRVR_NAME ) ;
gai - > num_busses = 1 ;
gai - > num_targsper_bus = ESAS2R_MAX_ID + 1 ;
gai - > num_lunsper_targ = 1 ;
}
}
u8 handle_hba_ioctl ( struct esas2r_adapter * a ,
struct atto_ioctl * ioctl_hba )
{
struct esas2r_buffered_ioctl bi ;
memset ( & bi , 0 , sizeof ( bi ) ) ;
bi . a = a ;
bi . ioctl = ioctl_hba ;
bi . length = sizeof ( struct atto_ioctl ) + ioctl_hba - > data_length ;
bi . callback = hba_ioctl_callback ;
bi . context = NULL ;
bi . done_callback = hba_ioctl_done_callback ;
bi . done_context = NULL ;
bi . offset = 0 ;
return handle_buffered_ioctl ( & bi ) ;
}
int esas2r_write_params ( struct esas2r_adapter * a , struct esas2r_request * rq ,
struct esas2r_sas_nvram * data )
{
int result = 0 ;
a - > nvram_command_done = 0 ;
rq - > comp_cb = complete_nvr_req ;
if ( esas2r_nvram_write ( a , rq , data ) ) {
/* now wait around for it to complete. */
while ( ! a - > nvram_command_done )
wait_event_interruptible ( a - > nvram_waiter ,
a - > nvram_command_done ) ;
;
/* done, check the status. */
if ( rq - > req_stat = = RS_SUCCESS )
result = 1 ;
}
return result ;
}
/* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
2019-02-07 19:07:20 +03:00
int esas2r_ioctl_handler ( void * hostdata , unsigned int cmd , void __user * arg )
2013-08-23 18:35:45 +04:00
{
struct atto_express_ioctl * ioctl = NULL ;
struct esas2r_adapter * a ;
struct esas2r_request * rq ;
u16 code ;
int err ;
esas2r_log ( ESAS2R_LOG_DEBG , " ioctl (%p, %x, %p) " , hostdata , cmd , arg ) ;
if ( ( arg = = NULL )
| | ( cmd < EXPRESS_IOCTL_MIN )
| | ( cmd > EXPRESS_IOCTL_MAX ) )
return - ENOTSUPP ;
2017-03-30 19:27:21 +03:00
ioctl = memdup_user ( arg , sizeof ( struct atto_express_ioctl ) ) ;
if ( IS_ERR ( ioctl ) ) {
2013-08-23 18:35:45 +04:00
esas2r_log ( ESAS2R_LOG_WARN ,
2019-02-07 19:07:20 +03:00
" ioctl_handler access_ok failed for cmd %u, address %p " ,
cmd , arg ) ;
2017-03-30 19:27:21 +03:00
return PTR_ERR ( ioctl ) ;
2013-08-23 18:35:45 +04:00
}
/* verify the signature */
if ( memcmp ( ioctl - > header . signature ,
EXPRESS_IOCTL_SIGNATURE ,
EXPRESS_IOCTL_SIGNATURE_SIZE ) ! = 0 ) {
esas2r_log ( ESAS2R_LOG_WARN , " invalid signature " ) ;
kfree ( ioctl ) ;
return - ENOTSUPP ;
}
/* assume success */
ioctl - > header . return_code = IOCTL_SUCCESS ;
err = 0 ;
/*
* handle EXPRESS_IOCTL_GET_CHANNELS
* without paying attention to channel
*/
if ( cmd = = EXPRESS_IOCTL_GET_CHANNELS ) {
int i = 0 , k = 0 ;
ioctl - > data . chanlist . num_channels = 0 ;
while ( i < MAX_ADAPTERS ) {
if ( esas2r_adapters [ i ] ) {
ioctl - > data . chanlist . num_channels + + ;
ioctl - > data . chanlist . channel [ k ] = i ;
k + + ;
}
i + + ;
}
goto ioctl_done ;
}
/* get the channel */
if ( ioctl - > header . channel = = 0xFF ) {
a = ( struct esas2r_adapter * ) hostdata ;
} else {
2016-02-15 22:01:29 +03:00
if ( ioctl - > header . channel > = MAX_ADAPTERS | |
esas2r_adapters [ ioctl - > header . channel ] = = NULL ) {
2013-08-23 18:35:45 +04:00
ioctl - > header . return_code = IOCTL_BAD_CHANNEL ;
esas2r_log ( ESAS2R_LOG_WARN , " bad channel value " ) ;
kfree ( ioctl ) ;
return - ENOTSUPP ;
}
2016-02-15 22:01:29 +03:00
a = esas2r_adapters [ ioctl - > header . channel ] ;
2013-08-23 18:35:45 +04:00
}
switch ( cmd ) {
case EXPRESS_IOCTL_RW_FIRMWARE :
if ( ioctl - > data . fwrw . img_type = = FW_IMG_FM_API ) {
err = esas2r_write_fw ( a ,
( char * ) ioctl - > data . fwrw . image ,
0 ,
sizeof ( struct
atto_express_ioctl ) ) ;
if ( err > = 0 ) {
err = esas2r_read_fw ( a ,
( char * ) ioctl - > data . fwrw .
image ,
0 ,
sizeof ( struct
atto_express_ioctl ) ) ;
}
} else if ( ioctl - > data . fwrw . img_type = = FW_IMG_FS_API ) {
err = esas2r_write_fs ( a ,
( char * ) ioctl - > data . fwrw . image ,
0 ,
sizeof ( struct
atto_express_ioctl ) ) ;
if ( err > = 0 ) {
err = esas2r_read_fs ( a ,
( char * ) ioctl - > data . fwrw .
image ,
0 ,
sizeof ( struct
atto_express_ioctl ) ) ;
}
} else {
ioctl - > header . return_code = IOCTL_BAD_FLASH_IMGTYPE ;
}
break ;
case EXPRESS_IOCTL_READ_PARAMS :
memcpy ( ioctl - > data . prw . data_buffer , a - > nvram ,
sizeof ( struct esas2r_sas_nvram ) ) ;
ioctl - > data . prw . code = 1 ;
break ;
case EXPRESS_IOCTL_WRITE_PARAMS :
rq = esas2r_alloc_request ( a ) ;
if ( rq = = NULL ) {
2014-11-16 16:35:33 +03:00
kfree ( ioctl ) ;
esas2r_log ( ESAS2R_LOG_WARN ,
" could not allocate an internal request " ) ;
return - ENOMEM ;
2013-08-23 18:35:45 +04:00
}
code = esas2r_write_params ( a , rq ,
( struct esas2r_sas_nvram * ) ioctl - > data . prw . data_buffer ) ;
ioctl - > data . prw . code = code ;
esas2r_free_request ( a , rq ) ;
break ;
case EXPRESS_IOCTL_DEFAULT_PARAMS :
esas2r_nvram_get_defaults ( a ,
( struct esas2r_sas_nvram * ) ioctl - > data . prw . data_buffer ) ;
ioctl - > data . prw . code = 1 ;
break ;
case EXPRESS_IOCTL_CHAN_INFO :
ioctl - > data . chaninfo . major_rev = ESAS2R_MAJOR_REV ;
ioctl - > data . chaninfo . minor_rev = ESAS2R_MINOR_REV ;
ioctl - > data . chaninfo . IRQ = a - > pcid - > irq ;
ioctl - > data . chaninfo . device_id = a - > pcid - > device ;
ioctl - > data . chaninfo . vendor_id = a - > pcid - > vendor ;
ioctl - > data . chaninfo . ven_dev_id = a - > pcid - > subsystem_device ;
ioctl - > data . chaninfo . revision_id = a - > pcid - > revision ;
ioctl - > data . chaninfo . pci_bus = a - > pcid - > bus - > number ;
ioctl - > data . chaninfo . pci_dev_func = a - > pcid - > devfn ;
ioctl - > data . chaninfo . core_rev = 0 ;
ioctl - > data . chaninfo . host_no = a - > host - > host_no ;
ioctl - > data . chaninfo . hbaapi_rev = 0 ;
break ;
case EXPRESS_IOCTL_SMP :
ioctl - > header . return_code = handle_smp_ioctl ( a ,
& ioctl - > data .
ioctl_smp ) ;
break ;
case EXPRESS_CSMI :
ioctl - > header . return_code =
handle_csmi_ioctl ( a , & ioctl - > data . csmi ) ;
break ;
case EXPRESS_IOCTL_HBA :
ioctl - > header . return_code = handle_hba_ioctl ( a ,
& ioctl - > data .
ioctl_hba ) ;
break ;
case EXPRESS_IOCTL_VDA :
err = esas2r_write_vda ( a ,
( char * ) & ioctl - > data . ioctl_vda ,
0 ,
sizeof ( struct atto_ioctl_vda ) +
ioctl - > data . ioctl_vda . data_length ) ;
if ( err > = 0 ) {
err = esas2r_read_vda ( a ,
( char * ) & ioctl - > data . ioctl_vda ,
0 ,
sizeof ( struct atto_ioctl_vda ) +
ioctl - > data . ioctl_vda . data_length ) ;
}
break ;
case EXPRESS_IOCTL_GET_MOD_INFO :
ioctl - > data . modinfo . adapter = a ;
ioctl - > data . modinfo . pci_dev = a - > pcid ;
ioctl - > data . modinfo . scsi_host = a - > host ;
ioctl - > data . modinfo . host_no = a - > host - > host_no ;
break ;
default :
esas2r_debug ( " esas2r_ioctl invalid cmd %p! " , cmd ) ;
ioctl - > header . return_code = IOCTL_ERR_INVCMD ;
}
ioctl_done :
if ( err < 0 ) {
2019-02-07 19:07:20 +03:00
esas2r_log ( ESAS2R_LOG_WARN , " err %d on ioctl cmd %u " , err ,
2013-08-23 18:35:45 +04:00
cmd ) ;
switch ( err ) {
case - ENOMEM :
case - EBUSY :
ioctl - > header . return_code = IOCTL_OUT_OF_RESOURCES ;
break ;
case - ENOSYS :
case - EINVAL :
ioctl - > header . return_code = IOCTL_INVALID_PARAM ;
break ;
2014-11-16 16:35:34 +03:00
default :
ioctl - > header . return_code = IOCTL_GENERAL_ERROR ;
break ;
2013-08-23 18:35:45 +04:00
}
}
/* Always copy the buffer back, if only to pick up the status */
2020-02-19 18:37:13 +03:00
err = copy_to_user ( arg , ioctl , sizeof ( struct atto_express_ioctl ) ) ;
2013-08-23 18:35:45 +04:00
if ( err ! = 0 ) {
esas2r_log ( ESAS2R_LOG_WARN ,
2019-02-07 19:07:20 +03:00
" ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u) " ,
err , cmd ) ;
2013-08-23 18:35:45 +04:00
kfree ( ioctl ) ;
return - EFAULT ;
}
kfree ( ioctl ) ;
return 0 ;
}
2019-02-07 19:07:20 +03:00
int esas2r_ioctl ( struct scsi_device * sd , unsigned int cmd , void __user * arg )
2013-08-23 18:35:45 +04:00
{
return esas2r_ioctl_handler ( sd - > host - > hostdata , cmd , arg ) ;
}
static void free_fw_buffers ( struct esas2r_adapter * a )
{
if ( a - > firmware . data ) {
dma_free_coherent ( & a - > pcid - > dev ,
( size_t ) a - > firmware . orig_len ,
a - > firmware . data ,
( dma_addr_t ) a - > firmware . phys ) ;
a - > firmware . data = NULL ;
}
}
static int allocate_fw_buffers ( struct esas2r_adapter * a , u32 length )
{
free_fw_buffers ( a ) ;
a - > firmware . orig_len = length ;
2020-08-20 21:14:11 +03:00
a - > firmware . data = dma_alloc_coherent ( & a - > pcid - > dev ,
( size_t ) length ,
( dma_addr_t * ) & a - > firmware . phys ,
GFP_KERNEL ) ;
2013-08-23 18:35:45 +04:00
if ( ! a - > firmware . data ) {
esas2r_debug ( " buffer alloc failed! " ) ;
return 0 ;
}
return 1 ;
}
/* Handle a call to read firmware. */
int esas2r_read_fw ( struct esas2r_adapter * a , char * buf , long off , int count )
{
esas2r_trace_enter ( ) ;
/* if the cached header is a status, simply copy it over and return. */
if ( a - > firmware . state = = FW_STATUS_ST ) {
int size = min_t ( int , count , sizeof ( a - > firmware . header ) ) ;
esas2r_trace_exit ( ) ;
memcpy ( buf , & a - > firmware . header , size ) ;
esas2r_debug ( " esas2r_read_fw: STATUS size %d " , size ) ;
return size ;
}
/*
* if the cached header is a command , do it if at
* offset 0 , otherwise copy the pieces .
*/
if ( a - > firmware . state = = FW_COMMAND_ST ) {
u32 length = a - > firmware . header . length ;
esas2r_trace_exit ( ) ;
esas2r_debug ( " esas2r_read_fw: COMMAND length %d off %d " ,
length ,
off ) ;
if ( off = = 0 ) {
if ( a - > firmware . header . action = = FI_ACT_UP ) {
if ( ! allocate_fw_buffers ( a , length ) )
return - ENOMEM ;
/* copy header over */
memcpy ( a - > firmware . data ,
& a - > firmware . header ,
sizeof ( a - > firmware . header ) ) ;
do_fm_api ( a ,
( struct esas2r_flash_img * ) a - > firmware . data ) ;
} else if ( a - > firmware . header . action = = FI_ACT_UPSZ ) {
int size =
min ( ( int ) count ,
( int ) sizeof ( a - > firmware . header ) ) ;
do_fm_api ( a , & a - > firmware . header ) ;
memcpy ( buf , & a - > firmware . header , size ) ;
esas2r_debug ( " FI_ACT_UPSZ size %d " , size ) ;
return size ;
} else {
esas2r_debug ( " invalid action %d " ,
a - > firmware . header . action ) ;
return - ENOSYS ;
}
}
if ( count + off > length )
count = length - off ;
if ( count < 0 )
return 0 ;
if ( ! a - > firmware . data ) {
esas2r_debug (
" read: nonzero offset but no buffer available! " ) ;
return - ENOMEM ;
}
esas2r_debug ( " esas2r_read_fw: off %d count %d length %d " , off ,
count ,
length ) ;
memcpy ( buf , & a - > firmware . data [ off ] , count ) ;
/* when done, release the buffer */
if ( length < = off + count ) {
esas2r_debug ( " esas2r_read_fw: freeing buffer! " ) ;
free_fw_buffers ( a ) ;
}
return count ;
}
esas2r_trace_exit ( ) ;
esas2r_debug ( " esas2r_read_fw: invalid firmware state %d " ,
a - > firmware . state ) ;
return - EINVAL ;
}
/* Handle a call to write firmware. */
int esas2r_write_fw ( struct esas2r_adapter * a , const char * buf , long off ,
int count )
{
u32 length ;
if ( off = = 0 ) {
struct esas2r_flash_img * header =
( struct esas2r_flash_img * ) buf ;
/* assume version 0 flash image */
int min_size = sizeof ( struct esas2r_flash_img_v0 ) ;
a - > firmware . state = FW_INVALID_ST ;
/* validate the version field first */
if ( count < 4
| | header - > fi_version > FI_VERSION_1 ) {
esas2r_debug (
" esas2r_write_fw: short header or invalid version " ) ;
return - EINVAL ;
}
/* See if its a version 1 flash image */
if ( header - > fi_version = = FI_VERSION_1 )
min_size = sizeof ( struct esas2r_flash_img ) ;
/* If this is the start, the header must be full and valid. */
if ( count < min_size ) {
esas2r_debug ( " esas2r_write_fw: short header, aborting " ) ;
return - EINVAL ;
}
/* Make sure the size is reasonable. */
length = header - > length ;
if ( length > 1024 * 1024 ) {
esas2r_debug (
" esas2r_write_fw: hosed, length %d fi_version %d " ,
length , header - > fi_version ) ;
return - EINVAL ;
}
/*
* If this is a write command , allocate memory because
* we have to cache everything . otherwise , just cache
* the header , because the read op will do the command .
*/
if ( header - > action = = FI_ACT_DOWN ) {
if ( ! allocate_fw_buffers ( a , length ) )
return - ENOMEM ;
/*
* Store the command , so there is context on subsequent
* calls .
*/
memcpy ( & a - > firmware . header ,
buf ,
sizeof ( * header ) ) ;
} else if ( header - > action = = FI_ACT_UP
| | header - > action = = FI_ACT_UPSZ ) {
/* Save the command, result will be picked up on read */
memcpy ( & a - > firmware . header ,
buf ,
sizeof ( * header ) ) ;
a - > firmware . state = FW_COMMAND_ST ;
esas2r_debug (
" esas2r_write_fw: COMMAND, count %d, action %d " ,
count , header - > action ) ;
/*
* Pretend we took the whole buffer ,
* so we don ' t get bothered again .
*/
return count ;
} else {
esas2r_debug ( " esas2r_write_fw: invalid action %d " ,
a - > firmware . header . action ) ;
return - ENOSYS ;
}
} else {
length = a - > firmware . header . length ;
}
/*
* We only get here on a download command , regardless of offset .
* the chunks written by the system need to be cached , and when
* the final one arrives , issue the fmapi command .
*/
if ( off + count > length )
count = length - off ;
if ( count > 0 ) {
esas2r_debug ( " esas2r_write_fw: off %d count %d length %d " , off ,
count ,
length ) ;
/*
* On a full upload , the system tries sending the whole buffer .
* there ' s nothing to do with it , so just drop it here , before
* trying to copy over into unallocated memory !
*/
if ( a - > firmware . header . action = = FI_ACT_UP )
return count ;
if ( ! a - > firmware . data ) {
esas2r_debug (
" write: nonzero offset but no buffer available! " ) ;
return - ENOMEM ;
}
memcpy ( & a - > firmware . data [ off ] , buf , count ) ;
if ( length = = off + count ) {
do_fm_api ( a ,
( struct esas2r_flash_img * ) a - > firmware . data ) ;
/*
* Now copy the header result to be picked up by the
* next read
*/
memcpy ( & a - > firmware . header ,
a - > firmware . data ,
sizeof ( a - > firmware . header ) ) ;
a - > firmware . state = FW_STATUS_ST ;
esas2r_debug ( " write completed " ) ;
/*
* Since the system has the data buffered , the only way
* this can leak is if a root user writes a program
* that writes a shorter buffer than it claims , and the
* copyin fails .
*/
free_fw_buffers ( a ) ;
}
}
return count ;
}
/* Callback for the completion of a VDA request. */
static void vda_complete_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
a - > vda_command_done = 1 ;
wake_up_interruptible ( & a - > vda_waiter ) ;
}
/* Scatter/gather callback for VDA requests */
static u32 get_physaddr_vda ( struct esas2r_sg_context * sgc , u64 * addr )
{
struct esas2r_adapter * a = ( struct esas2r_adapter * ) sgc - > adapter ;
int offset = ( u8 * ) sgc - > cur_offset - ( u8 * ) a - > vda_buffer ;
( * addr ) = a - > ppvda_buffer + offset ;
return VDA_MAX_BUFFER_SIZE - offset ;
}
/* Handle a call to read a VDA command. */
int esas2r_read_vda ( struct esas2r_adapter * a , char * buf , long off , int count )
{
if ( ! a - > vda_buffer )
return - ENOMEM ;
if ( off = = 0 ) {
struct esas2r_request * rq ;
struct atto_ioctl_vda * vi =
( struct atto_ioctl_vda * ) a - > vda_buffer ;
struct esas2r_sg_context sgc ;
bool wait_for_completion ;
/*
* Presumeably , someone has already written to the vda_buffer ,
* and now they are reading the node the response , so now we
* will actually issue the request to the chip and reply .
*/
/* allocate a request */
rq = esas2r_alloc_request ( a ) ;
if ( rq = = NULL ) {
2018-05-09 00:54:35 +03:00
esas2r_debug ( " esas2r_read_vda: out of requests " ) ;
2013-08-23 18:35:45 +04:00
return - EBUSY ;
}
rq - > comp_cb = vda_complete_req ;
sgc . first_req = rq ;
sgc . adapter = a ;
sgc . cur_offset = a - > vda_buffer + VDA_BUFFER_HEADER_SZ ;
sgc . get_phys_addr = ( PGETPHYSADDR ) get_physaddr_vda ;
a - > vda_command_done = 0 ;
wait_for_completion =
esas2r_process_vda_ioctl ( a , vi , rq , & sgc ) ;
if ( wait_for_completion ) {
/* now wait around for it to complete. */
while ( ! a - > vda_command_done )
wait_event_interruptible ( a - > vda_waiter ,
a - > vda_command_done ) ;
}
esas2r_free_request ( a , ( struct esas2r_request * ) rq ) ;
}
if ( off > VDA_MAX_BUFFER_SIZE )
return 0 ;
if ( count + off > VDA_MAX_BUFFER_SIZE )
count = VDA_MAX_BUFFER_SIZE - off ;
if ( count < 0 )
return 0 ;
memcpy ( buf , a - > vda_buffer + off , count ) ;
return count ;
}
/* Handle a call to write a VDA command. */
int esas2r_write_vda ( struct esas2r_adapter * a , const char * buf , long off ,
int count )
{
/*
* allocate memory for it , if not already done . once allocated ,
* we will keep it around until the driver is unloaded .
*/
if ( ! a - > vda_buffer ) {
dma_addr_t dma_addr ;
2020-08-20 21:14:11 +03:00
a - > vda_buffer = dma_alloc_coherent ( & a - > pcid - > dev ,
( size_t )
VDA_MAX_BUFFER_SIZE ,
& dma_addr ,
GFP_KERNEL ) ;
2013-08-23 18:35:45 +04:00
a - > ppvda_buffer = dma_addr ;
}
if ( ! a - > vda_buffer )
return - ENOMEM ;
if ( off > VDA_MAX_BUFFER_SIZE )
return 0 ;
if ( count + off > VDA_MAX_BUFFER_SIZE )
count = VDA_MAX_BUFFER_SIZE - off ;
if ( count < 1 )
return 0 ;
memcpy ( a - > vda_buffer + off , buf , count ) ;
return count ;
}
/* Callback for the completion of an FS_API request.*/
static void fs_api_complete_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
a - > fs_api_command_done = 1 ;
wake_up_interruptible ( & a - > fs_api_waiter ) ;
}
/* Scatter/gather callback for VDA requests */
static u32 get_physaddr_fs_api ( struct esas2r_sg_context * sgc , u64 * addr )
{
struct esas2r_adapter * a = ( struct esas2r_adapter * ) sgc - > adapter ;
struct esas2r_ioctl_fs * fs =
( struct esas2r_ioctl_fs * ) a - > fs_api_buffer ;
u32 offset = ( u8 * ) sgc - > cur_offset - ( u8 * ) fs ;
( * addr ) = a - > ppfs_api_buffer + offset ;
return a - > fs_api_buffer_size - offset ;
}
/* Handle a call to read firmware via FS_API. */
int esas2r_read_fs ( struct esas2r_adapter * a , char * buf , long off , int count )
{
if ( ! a - > fs_api_buffer )
return - ENOMEM ;
if ( off = = 0 ) {
struct esas2r_request * rq ;
struct esas2r_sg_context sgc ;
struct esas2r_ioctl_fs * fs =
( struct esas2r_ioctl_fs * ) a - > fs_api_buffer ;
/* If another flash request is already in progress, return. */
2017-06-08 13:07:31 +03:00
if ( mutex_lock_interruptible ( & a - > fs_api_mutex ) ) {
2013-08-23 18:35:45 +04:00
busy :
fs - > status = ATTO_STS_OUT_OF_RSRC ;
return - EBUSY ;
}
/*
* Presumeably , someone has already written to the
* fs_api_buffer , and now they are reading the node the
* response , so now we will actually issue the request to the
* chip and reply . Allocate a request
*/
rq = esas2r_alloc_request ( a ) ;
if ( rq = = NULL ) {
esas2r_debug ( " esas2r_read_fs: out of requests " ) ;
2017-06-08 13:07:31 +03:00
mutex_unlock ( & a - > fs_api_mutex ) ;
2013-08-23 18:35:45 +04:00
goto busy ;
}
rq - > comp_cb = fs_api_complete_req ;
/* Set up the SGCONTEXT for to build the s/g table */
sgc . cur_offset = fs - > data ;
sgc . get_phys_addr = ( PGETPHYSADDR ) get_physaddr_fs_api ;
a - > fs_api_command_done = 0 ;
if ( ! esas2r_process_fs_ioctl ( a , fs , rq , & sgc ) ) {
if ( fs - > status = = ATTO_STS_OUT_OF_RSRC )
count = - EBUSY ;
goto dont_wait ;
}
/* Now wait around for it to complete. */
while ( ! a - > fs_api_command_done )
wait_event_interruptible ( a - > fs_api_waiter ,
a - > fs_api_command_done ) ;
;
dont_wait :
/* Free the request and keep going */
2017-06-08 13:07:31 +03:00
mutex_unlock ( & a - > fs_api_mutex ) ;
2013-08-23 18:35:45 +04:00
esas2r_free_request ( a , ( struct esas2r_request * ) rq ) ;
/* Pick up possible error code from above */
if ( count < 0 )
return count ;
}
if ( off > a - > fs_api_buffer_size )
return 0 ;
if ( count + off > a - > fs_api_buffer_size )
count = a - > fs_api_buffer_size - off ;
if ( count < 0 )
return 0 ;
memcpy ( buf , a - > fs_api_buffer + off , count ) ;
return count ;
}
/* Handle a call to write firmware via FS_API. */
int esas2r_write_fs ( struct esas2r_adapter * a , const char * buf , long off ,
int count )
{
if ( off = = 0 ) {
struct esas2r_ioctl_fs * fs = ( struct esas2r_ioctl_fs * ) buf ;
u32 length = fs - > command . length + offsetof (
struct esas2r_ioctl_fs ,
data ) ;
/*
* Special case , for BEGIN commands , the length field
* is lying to us , so just get enough for the header .
*/
if ( fs - > command . command = = ESAS2R_FS_CMD_BEGINW )
length = offsetof ( struct esas2r_ioctl_fs , data ) ;
/*
* Beginning a command . We assume we ' ll get at least
* enough in the first write so we can look at the
* header and see how much we need to alloc .
*/
if ( count < offsetof ( struct esas2r_ioctl_fs , data ) )
return - EINVAL ;
/* Allocate a buffer or use the existing buffer. */
if ( a - > fs_api_buffer ) {
if ( a - > fs_api_buffer_size < length ) {
/* Free too-small buffer and get a new one */
dma_free_coherent ( & a - > pcid - > dev ,
( size_t ) a - > fs_api_buffer_size ,
a - > fs_api_buffer ,
( dma_addr_t ) a - > ppfs_api_buffer ) ;
goto re_allocate_buffer ;
}
} else {
re_allocate_buffer :
a - > fs_api_buffer_size = length ;
2020-08-20 21:14:11 +03:00
a - > fs_api_buffer = dma_alloc_coherent ( & a - > pcid - > dev ,
( size_t ) a - > fs_api_buffer_size ,
( dma_addr_t * ) & a - > ppfs_api_buffer ,
GFP_KERNEL ) ;
2013-08-23 18:35:45 +04:00
}
}
if ( ! a - > fs_api_buffer )
return - ENOMEM ;
if ( off > a - > fs_api_buffer_size )
return 0 ;
if ( count + off > a - > fs_api_buffer_size )
count = a - > fs_api_buffer_size - off ;
if ( count < 1 )
return 0 ;
memcpy ( a - > fs_api_buffer + off , buf , count ) ;
return count ;
}