2023-10-09 14:33:22 +08:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel La Jolla Cove Adapter USB driver
*
* Copyright ( c ) 2023 , Intel Corporation .
*/
# include <linux/acpi.h>
# include <linux/auxiliary_bus.h>
# include <linux/dev_printk.h>
# include <linux/kernel.h>
# include <linux/mod_devicetable.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/types.h>
# include <linux/usb.h>
# include <linux/usb/ljca.h>
# include <asm/unaligned.h>
/* command flags */
# define LJCA_ACK_FLAG BIT(0)
# define LJCA_RESP_FLAG BIT(1)
# define LJCA_CMPL_FLAG BIT(2)
# define LJCA_MAX_PACKET_SIZE 64u
# define LJCA_MAX_PAYLOAD_SIZE \
( LJCA_MAX_PACKET_SIZE - sizeof ( struct ljca_msg ) )
# define LJCA_WRITE_TIMEOUT_MS 200
# define LJCA_WRITE_ACK_TIMEOUT_MS 500
# define LJCA_ENUM_CLIENT_TIMEOUT_MS 20
/* ljca client type */
enum ljca_client_type {
LJCA_CLIENT_MNG = 1 ,
LJCA_CLIENT_GPIO = 3 ,
LJCA_CLIENT_I2C = 4 ,
LJCA_CLIENT_SPI = 5 ,
} ;
/* MNG client commands */
enum ljca_mng_cmd {
LJCA_MNG_RESET = 2 ,
LJCA_MNG_ENUM_GPIO = 4 ,
LJCA_MNG_ENUM_I2C = 5 ,
LJCA_MNG_ENUM_SPI = 8 ,
} ;
/* ljca client acpi _ADR */
enum ljca_client_acpi_adr {
LJCA_GPIO_ACPI_ADR ,
LJCA_I2C1_ACPI_ADR ,
LJCA_I2C2_ACPI_ADR ,
LJCA_SPI1_ACPI_ADR ,
LJCA_SPI2_ACPI_ADR ,
LJCA_CLIENT_ACPI_ADR_MAX ,
} ;
/* ljca cmd message structure */
struct ljca_msg {
u8 type ;
u8 cmd ;
u8 flags ;
u8 len ;
u8 data [ ] __counted_by ( len ) ;
} __packed ;
struct ljca_i2c_ctr_info {
u8 id ;
u8 capacity ;
u8 intr_pin ;
} __packed ;
struct ljca_i2c_descriptor {
u8 num ;
struct ljca_i2c_ctr_info info [ ] __counted_by ( num ) ;
} __packed ;
struct ljca_spi_ctr_info {
u8 id ;
u8 capacity ;
u8 intr_pin ;
} __packed ;
struct ljca_spi_descriptor {
u8 num ;
struct ljca_spi_ctr_info info [ ] __counted_by ( num ) ;
} __packed ;
struct ljca_bank_descriptor {
u8 bank_id ;
u8 pin_num ;
/* 1 bit for each gpio, 1 means valid */
__le32 valid_pins ;
} __packed ;
struct ljca_gpio_descriptor {
u8 pins_per_bank ;
u8 bank_num ;
struct ljca_bank_descriptor bank_desc [ ] __counted_by ( bank_num ) ;
} __packed ;
/**
* struct ljca_adapter - represent a ljca adapter
*
* @ intf : the usb interface for this ljca adapter
* @ usb_dev : the usb device for this ljca adapter
* @ dev : the specific device info of the usb interface
* @ rx_pipe : bulk in pipe for receive data from firmware
* @ tx_pipe : bulk out pipe for send data to firmware
* @ rx_urb : urb used for the bulk in pipe
* @ rx_buf : buffer used to receive command response and event
* @ rx_len : length of rx buffer
* @ ex_buf : external buffer to save command response
* @ ex_buf_len : length of external buffer
* @ actual_length : actual length of data copied to external buffer
* @ tx_buf : buffer used to download command to firmware
* @ tx_buf_len : length of tx buffer
* @ lock : spinlock to protect tx_buf and ex_buf
* @ cmd_completion : completion object as the command receives ack
* @ mutex : mutex to avoid command download concurrently
* @ client_list : client device list
* @ disconnect : usb disconnect ongoing or not
* @ reset_id : used to reset firmware
*/
struct ljca_adapter {
struct usb_interface * intf ;
struct usb_device * usb_dev ;
struct device * dev ;
unsigned int rx_pipe ;
unsigned int tx_pipe ;
struct urb * rx_urb ;
void * rx_buf ;
unsigned int rx_len ;
u8 * ex_buf ;
u8 ex_buf_len ;
u8 actual_length ;
void * tx_buf ;
u8 tx_buf_len ;
spinlock_t lock ;
struct completion cmd_completion ;
struct mutex mutex ;
struct list_head client_list ;
bool disconnect ;
u32 reset_id ;
} ;
struct ljca_match_ids_walk_data {
const struct acpi_device_id * ids ;
const char * uid ;
struct acpi_device * adev ;
} ;
static const struct acpi_device_id ljca_gpio_hids [ ] = {
{ " INTC1074 " } ,
{ " INTC1096 " } ,
{ " INTC100B " } ,
{ " INTC10D1 " } ,
{ } ,
} ;
static const struct acpi_device_id ljca_i2c_hids [ ] = {
{ " INTC1075 " } ,
{ " INTC1097 " } ,
{ " INTC100C " } ,
{ " INTC10D2 " } ,
{ } ,
} ;
static const struct acpi_device_id ljca_spi_hids [ ] = {
{ " INTC1091 " } ,
{ " INTC1098 " } ,
{ " INTC100D " } ,
{ " INTC10D3 " } ,
{ } ,
} ;
static void ljca_handle_event ( struct ljca_adapter * adap ,
struct ljca_msg * header )
{
struct ljca_client * client ;
list_for_each_entry ( client , & adap - > client_list , link ) {
/*
* Currently only GPIO register event callback , but
* firmware message structure should include id when
* multiple same type clients register event callback .
*/
if ( client - > type = = header - > type ) {
unsigned long flags ;
spin_lock_irqsave ( & client - > event_cb_lock , flags ) ;
client - > event_cb ( client - > context , header - > cmd ,
header - > data , header - > len ) ;
spin_unlock_irqrestore ( & client - > event_cb_lock , flags ) ;
break ;
}
}
}
/* process command ack and received data if available */
static void ljca_handle_cmd_ack ( struct ljca_adapter * adap , struct ljca_msg * header )
{
struct ljca_msg * tx_header = adap - > tx_buf ;
u8 ibuf_len , actual_len = 0 ;
unsigned long flags ;
u8 * ibuf ;
spin_lock_irqsave ( & adap - > lock , flags ) ;
if ( tx_header - > type ! = header - > type | | tx_header - > cmd ! = header - > cmd ) {
spin_unlock_irqrestore ( & adap - > lock , flags ) ;
dev_err ( adap - > dev , " cmd ack mismatch error \n " ) ;
return ;
}
ibuf_len = adap - > ex_buf_len ;
ibuf = adap - > ex_buf ;
if ( ibuf & & ibuf_len ) {
actual_len = min ( header - > len , ibuf_len ) ;
/* copy received data to external buffer */
memcpy ( ibuf , header - > data , actual_len ) ;
}
/* update copied data length */
adap - > actual_length = actual_len ;
spin_unlock_irqrestore ( & adap - > lock , flags ) ;
complete ( & adap - > cmd_completion ) ;
}
static void ljca_recv ( struct urb * urb )
{
struct ljca_msg * header = urb - > transfer_buffer ;
struct ljca_adapter * adap = urb - > context ;
int ret ;
switch ( urb - > status ) {
case 0 :
/* success */
break ;
case - ENOENT :
/*
* directly complete the possible ongoing transfer
* during disconnect
*/
if ( adap - > disconnect )
complete ( & adap - > cmd_completion ) ;
return ;
case - ECONNRESET :
case - ESHUTDOWN :
case - EPIPE :
/* rx urb is terminated */
dev_dbg ( adap - > dev , " rx urb terminated with status: %d \n " ,
urb - > status ) ;
return ;
default :
dev_dbg ( adap - > dev , " rx urb error: %d \n " , urb - > status ) ;
goto resubmit ;
}
if ( header - > len + sizeof ( * header ) ! = urb - > actual_length )
goto resubmit ;
if ( header - > flags & LJCA_ACK_FLAG )
ljca_handle_cmd_ack ( adap , header ) ;
else
ljca_handle_event ( adap , header ) ;
resubmit :
ret = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( ret & & ret ! = - EPERM )
dev_err ( adap - > dev , " resubmit rx urb error %d \n " , ret ) ;
}
static int ljca_send ( struct ljca_adapter * adap , u8 type , u8 cmd ,
const u8 * obuf , u8 obuf_len , u8 * ibuf , u8 ibuf_len ,
bool ack , unsigned long timeout )
{
unsigned int msg_len = sizeof ( struct ljca_msg ) + obuf_len ;
struct ljca_msg * header = adap - > tx_buf ;
unsigned int transferred ;
unsigned long flags ;
int ret ;
if ( adap - > disconnect )
return - ENODEV ;
if ( msg_len > adap - > tx_buf_len )
return - EINVAL ;
mutex_lock ( & adap - > mutex ) ;
spin_lock_irqsave ( & adap - > lock , flags ) ;
header - > type = type ;
header - > cmd = cmd ;
header - > len = obuf_len ;
if ( obuf )
memcpy ( header - > data , obuf , obuf_len ) ;
header - > flags = LJCA_CMPL_FLAG | ( ack ? LJCA_ACK_FLAG : 0 ) ;
adap - > ex_buf = ibuf ;
adap - > ex_buf_len = ibuf_len ;
adap - > actual_length = 0 ;
spin_unlock_irqrestore ( & adap - > lock , flags ) ;
reinit_completion ( & adap - > cmd_completion ) ;
ret = usb_autopm_get_interface ( adap - > intf ) ;
if ( ret < 0 )
goto out ;
ret = usb_bulk_msg ( adap - > usb_dev , adap - > tx_pipe , header ,
msg_len , & transferred , LJCA_WRITE_TIMEOUT_MS ) ;
usb_autopm_put_interface ( adap - > intf ) ;
if ( ret < 0 )
goto out ;
if ( transferred ! = msg_len ) {
ret = - EIO ;
goto out ;
}
if ( ack ) {
ret = wait_for_completion_timeout ( & adap - > cmd_completion ,
timeout ) ;
if ( ! ret ) {
ret = - ETIMEDOUT ;
goto out ;
}
}
ret = adap - > actual_length ;
out :
spin_lock_irqsave ( & adap - > lock , flags ) ;
adap - > ex_buf = NULL ;
adap - > ex_buf_len = 0 ;
memset ( header , 0 , sizeof ( * header ) ) ;
spin_unlock_irqrestore ( & adap - > lock , flags ) ;
mutex_unlock ( & adap - > mutex ) ;
return ret ;
}
int ljca_transfer ( struct ljca_client * client , u8 cmd , const u8 * obuf ,
u8 obuf_len , u8 * ibuf , u8 ibuf_len )
{
return ljca_send ( client - > adapter , client - > type , cmd ,
obuf , obuf_len , ibuf , ibuf_len , true ,
LJCA_WRITE_ACK_TIMEOUT_MS ) ;
}
EXPORT_SYMBOL_NS_GPL ( ljca_transfer , LJCA ) ;
int ljca_transfer_noack ( struct ljca_client * client , u8 cmd , const u8 * obuf ,
u8 obuf_len )
{
return ljca_send ( client - > adapter , client - > type , cmd , obuf ,
obuf_len , NULL , 0 , false , LJCA_WRITE_ACK_TIMEOUT_MS ) ;
}
EXPORT_SYMBOL_NS_GPL ( ljca_transfer_noack , LJCA ) ;
int ljca_register_event_cb ( struct ljca_client * client , ljca_event_cb_t event_cb ,
void * context )
{
unsigned long flags ;
if ( ! event_cb )
return - EINVAL ;
spin_lock_irqsave ( & client - > event_cb_lock , flags ) ;
if ( client - > event_cb ) {
spin_unlock_irqrestore ( & client - > event_cb_lock , flags ) ;
return - EALREADY ;
}
client - > event_cb = event_cb ;
client - > context = context ;
spin_unlock_irqrestore ( & client - > event_cb_lock , flags ) ;
return 0 ;
}
EXPORT_SYMBOL_NS_GPL ( ljca_register_event_cb , LJCA ) ;
void ljca_unregister_event_cb ( struct ljca_client * client )
{
unsigned long flags ;
spin_lock_irqsave ( & client - > event_cb_lock , flags ) ;
client - > event_cb = NULL ;
client - > context = NULL ;
spin_unlock_irqrestore ( & client - > event_cb_lock , flags ) ;
}
EXPORT_SYMBOL_NS_GPL ( ljca_unregister_event_cb , LJCA ) ;
static int ljca_match_device_ids ( struct acpi_device * adev , void * data )
{
struct ljca_match_ids_walk_data * wd = data ;
const char * uid = acpi_device_uid ( adev ) ;
if ( acpi_match_device_ids ( adev , wd - > ids ) )
return 0 ;
if ( ! wd - > uid )
goto match ;
if ( ! uid )
/*
* Some DSDTs have only one ACPI companion for the two I2C
* controllers and they don ' t set a UID at all ( e . g . Dell
* Latitude 9420 ) . On these platforms only the first I2C
* controller is used , so if a HID match has no UID we use
* " 0 " as the UID and assign ACPI companion to the first
* I2C controller .
*/
uid = " 0 " ;
else
uid = strchr ( uid , wd - > uid [ 0 ] ) ;
if ( ! uid | | strcmp ( uid , wd - > uid ) )
return 0 ;
match :
wd - > adev = adev ;
return 1 ;
}
/* bind auxiliary device to acpi device */
static void ljca_auxdev_acpi_bind ( struct ljca_adapter * adap ,
struct auxiliary_device * auxdev ,
u64 adr , u8 id )
{
struct ljca_match_ids_walk_data wd = { 0 } ;
struct device * dev = adap - > dev ;
2023-11-14 15:25:31 +08:00
struct acpi_device * parent ;
2023-10-09 14:33:22 +08:00
char uid [ 4 ] ;
parent = ACPI_COMPANION ( dev ) ;
if ( ! parent )
return ;
/*
2023-11-14 15:25:31 +08:00
* Currently LJCA hw doesn ' t use _ADR instead the shipped
2023-10-09 14:33:22 +08:00
* platforms use _HID to distinguish children devices .
*/
switch ( adr ) {
case LJCA_GPIO_ACPI_ADR :
wd . ids = ljca_gpio_hids ;
break ;
case LJCA_I2C1_ACPI_ADR :
case LJCA_I2C2_ACPI_ADR :
snprintf ( uid , sizeof ( uid ) , " %d " , id ) ;
wd . uid = uid ;
wd . ids = ljca_i2c_hids ;
break ;
case LJCA_SPI1_ACPI_ADR :
case LJCA_SPI2_ACPI_ADR :
wd . ids = ljca_spi_hids ;
break ;
default :
dev_warn ( dev , " unsupported _ADR \n " ) ;
return ;
}
acpi_dev_for_each_child ( parent , ljca_match_device_ids , & wd ) ;
if ( wd . adev ) {
ACPI_COMPANION_SET ( & auxdev - > dev , wd . adev ) ;
return ;
}
parent = ACPI_COMPANION ( dev - > parent - > parent ) ;
if ( ! parent )
return ;
acpi_dev_for_each_child ( parent , ljca_match_device_ids , & wd ) ;
if ( wd . adev )
ACPI_COMPANION_SET ( & auxdev - > dev , wd . adev ) ;
}
static void ljca_auxdev_release ( struct device * dev )
{
struct auxiliary_device * auxdev = to_auxiliary_dev ( dev ) ;
kfree ( auxdev - > dev . platform_data ) ;
}
static int ljca_new_client_device ( struct ljca_adapter * adap , u8 type , u8 id ,
char * name , void * data , u64 adr )
{
struct auxiliary_device * auxdev ;
struct ljca_client * client ;
int ret ;
client = kzalloc ( sizeof * client , GFP_KERNEL ) ;
if ( ! client )
return - ENOMEM ;
client - > type = type ;
client - > id = id ;
client - > adapter = adap ;
spin_lock_init ( & client - > event_cb_lock ) ;
auxdev = & client - > auxdev ;
auxdev - > name = name ;
auxdev - > id = id ;
auxdev - > dev . parent = adap - > dev ;
auxdev - > dev . platform_data = data ;
auxdev - > dev . release = ljca_auxdev_release ;
ret = auxiliary_device_init ( auxdev ) ;
if ( ret )
goto err_free ;
ljca_auxdev_acpi_bind ( adap , auxdev , adr , id ) ;
ret = auxiliary_device_add ( auxdev ) ;
if ( ret )
goto err_uninit ;
list_add_tail ( & client - > link , & adap - > client_list ) ;
return 0 ;
err_uninit :
auxiliary_device_uninit ( auxdev ) ;
err_free :
kfree ( client ) ;
return ret ;
}
static int ljca_enumerate_gpio ( struct ljca_adapter * adap )
{
u32 valid_pin [ LJCA_MAX_GPIO_NUM / BITS_PER_TYPE ( u32 ) ] ;
struct ljca_gpio_descriptor * desc ;
struct ljca_gpio_info * gpio_info ;
u8 buf [ LJCA_MAX_PAYLOAD_SIZE ] ;
int ret , gpio_num ;
unsigned int i ;
ret = ljca_send ( adap , LJCA_CLIENT_MNG , LJCA_MNG_ENUM_GPIO , NULL , 0 , buf ,
sizeof ( buf ) , true , LJCA_ENUM_CLIENT_TIMEOUT_MS ) ;
if ( ret < 0 )
return ret ;
/* check firmware response */
desc = ( struct ljca_gpio_descriptor * ) buf ;
if ( ret ! = struct_size ( desc , bank_desc , desc - > bank_num ) )
return - EINVAL ;
gpio_num = desc - > pins_per_bank * desc - > bank_num ;
if ( gpio_num > LJCA_MAX_GPIO_NUM )
return - EINVAL ;
/* construct platform data */
gpio_info = kzalloc ( sizeof * gpio_info , GFP_KERNEL ) ;
if ( ! gpio_info )
return - ENOMEM ;
gpio_info - > num = gpio_num ;
for ( i = 0 ; i < desc - > bank_num ; i + + )
valid_pin [ i ] = get_unaligned_le32 ( & desc - > bank_desc [ i ] . valid_pins ) ;
bitmap_from_arr32 ( gpio_info - > valid_pin_map , valid_pin , gpio_num ) ;
ret = ljca_new_client_device ( adap , LJCA_CLIENT_GPIO , 0 , " ljca-gpio " ,
gpio_info , LJCA_GPIO_ACPI_ADR ) ;
if ( ret )
kfree ( gpio_info ) ;
return ret ;
}
static int ljca_enumerate_i2c ( struct ljca_adapter * adap )
{
struct ljca_i2c_descriptor * desc ;
struct ljca_i2c_info * i2c_info ;
u8 buf [ LJCA_MAX_PAYLOAD_SIZE ] ;
unsigned int i ;
int ret ;
ret = ljca_send ( adap , LJCA_CLIENT_MNG , LJCA_MNG_ENUM_I2C , NULL , 0 , buf ,
sizeof ( buf ) , true , LJCA_ENUM_CLIENT_TIMEOUT_MS ) ;
if ( ret < 0 )
return ret ;
/* check firmware response */
desc = ( struct ljca_i2c_descriptor * ) buf ;
if ( ret ! = struct_size ( desc , info , desc - > num ) )
return - EINVAL ;
for ( i = 0 ; i < desc - > num ; i + + ) {
/* construct platform data */
i2c_info = kzalloc ( sizeof * i2c_info , GFP_KERNEL ) ;
if ( ! i2c_info )
return - ENOMEM ;
i2c_info - > id = desc - > info [ i ] . id ;
i2c_info - > capacity = desc - > info [ i ] . capacity ;
i2c_info - > intr_pin = desc - > info [ i ] . intr_pin ;
ret = ljca_new_client_device ( adap , LJCA_CLIENT_I2C , i ,
" ljca-i2c " , i2c_info ,
LJCA_I2C1_ACPI_ADR + i ) ;
if ( ret ) {
kfree ( i2c_info ) ;
return ret ;
}
}
return 0 ;
}
static int ljca_enumerate_spi ( struct ljca_adapter * adap )
{
struct ljca_spi_descriptor * desc ;
struct ljca_spi_info * spi_info ;
u8 buf [ LJCA_MAX_PAYLOAD_SIZE ] ;
unsigned int i ;
int ret ;
2023-11-21 21:32:05 +01:00
/* Not all LJCA chips implement SPI, a timeout reading the descriptors is normal */
2023-10-09 14:33:22 +08:00
ret = ljca_send ( adap , LJCA_CLIENT_MNG , LJCA_MNG_ENUM_SPI , NULL , 0 , buf ,
sizeof ( buf ) , true , LJCA_ENUM_CLIENT_TIMEOUT_MS ) ;
if ( ret < 0 )
2023-11-21 21:32:05 +01:00
return ( ret = = - ETIMEDOUT ) ? 0 : ret ;
2023-10-09 14:33:22 +08:00
/* check firmware response */
desc = ( struct ljca_spi_descriptor * ) buf ;
if ( ret ! = struct_size ( desc , info , desc - > num ) )
return - EINVAL ;
for ( i = 0 ; i < desc - > num ; i + + ) {
/* construct platform data */
spi_info = kzalloc ( sizeof * spi_info , GFP_KERNEL ) ;
if ( ! spi_info )
return - ENOMEM ;
spi_info - > id = desc - > info [ i ] . id ;
spi_info - > capacity = desc - > info [ i ] . capacity ;
ret = ljca_new_client_device ( adap , LJCA_CLIENT_SPI , i ,
" ljca-spi " , spi_info ,
LJCA_SPI1_ACPI_ADR + i ) ;
if ( ret ) {
kfree ( spi_info ) ;
return ret ;
}
}
return 0 ;
}
static int ljca_reset_handshake ( struct ljca_adapter * adap )
{
__le32 reset_id = cpu_to_le32 ( adap - > reset_id ) ;
__le32 reset_id_ret = 0 ;
int ret ;
adap - > reset_id + + ;
ret = ljca_send ( adap , LJCA_CLIENT_MNG , LJCA_MNG_RESET , ( u8 * ) & reset_id ,
sizeof ( __le32 ) , ( u8 * ) & reset_id_ret , sizeof ( __le32 ) ,
true , LJCA_WRITE_ACK_TIMEOUT_MS ) ;
if ( ret < 0 )
return ret ;
if ( reset_id_ret ! = reset_id )
return - EINVAL ;
return 0 ;
}
static int ljca_enumerate_clients ( struct ljca_adapter * adap )
{
struct ljca_client * client , * next ;
int ret ;
ret = ljca_reset_handshake ( adap ) ;
if ( ret )
goto err_kill ;
ret = ljca_enumerate_gpio ( adap ) ;
if ( ret ) {
dev_err ( adap - > dev , " enumerate GPIO error \n " ) ;
goto err_kill ;
}
ret = ljca_enumerate_i2c ( adap ) ;
if ( ret ) {
dev_err ( adap - > dev , " enumerate I2C error \n " ) ;
goto err_kill ;
}
ret = ljca_enumerate_spi ( adap ) ;
if ( ret ) {
dev_err ( adap - > dev , " enumerate SPI error \n " ) ;
goto err_kill ;
}
return 0 ;
err_kill :
adap - > disconnect = true ;
usb_kill_urb ( adap - > rx_urb ) ;
list_for_each_entry_safe_reverse ( client , next , & adap - > client_list , link ) {
auxiliary_device_delete ( & client - > auxdev ) ;
auxiliary_device_uninit ( & client - > auxdev ) ;
list_del_init ( & client - > link ) ;
kfree ( client ) ;
}
return ret ;
}
static int ljca_probe ( struct usb_interface * interface ,
const struct usb_device_id * id )
{
struct usb_device * usb_dev = interface_to_usbdev ( interface ) ;
struct usb_host_interface * alt = interface - > cur_altsetting ;
struct usb_endpoint_descriptor * ep_in , * ep_out ;
struct device * dev = & interface - > dev ;
struct ljca_adapter * adap ;
int ret ;
adap = devm_kzalloc ( dev , sizeof ( * adap ) , GFP_KERNEL ) ;
if ( ! adap )
return - ENOMEM ;
/* separate tx buffer allocation for alignment */
adap - > tx_buf = devm_kzalloc ( dev , LJCA_MAX_PACKET_SIZE , GFP_KERNEL ) ;
if ( ! adap - > tx_buf )
return - ENOMEM ;
adap - > tx_buf_len = LJCA_MAX_PACKET_SIZE ;
mutex_init ( & adap - > mutex ) ;
spin_lock_init ( & adap - > lock ) ;
init_completion ( & adap - > cmd_completion ) ;
INIT_LIST_HEAD ( & adap - > client_list ) ;
adap - > intf = usb_get_intf ( interface ) ;
adap - > usb_dev = usb_dev ;
adap - > dev = dev ;
/*
* find the first bulk in and out endpoints .
* ignore any others .
*/
ret = usb_find_common_endpoints ( alt , & ep_in , & ep_out , NULL , NULL ) ;
if ( ret ) {
dev_err ( dev , " bulk endpoints not found \n " ) ;
goto err_put ;
}
adap - > rx_pipe = usb_rcvbulkpipe ( usb_dev , usb_endpoint_num ( ep_in ) ) ;
adap - > tx_pipe = usb_sndbulkpipe ( usb_dev , usb_endpoint_num ( ep_out ) ) ;
/* setup rx buffer */
adap - > rx_len = usb_endpoint_maxp ( ep_in ) ;
adap - > rx_buf = devm_kzalloc ( dev , adap - > rx_len , GFP_KERNEL ) ;
if ( ! adap - > rx_buf ) {
ret = - ENOMEM ;
goto err_put ;
}
/* alloc rx urb */
adap - > rx_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! adap - > rx_urb ) {
ret = - ENOMEM ;
goto err_put ;
}
usb_fill_bulk_urb ( adap - > rx_urb , usb_dev , adap - > rx_pipe ,
adap - > rx_buf , adap - > rx_len , ljca_recv , adap ) ;
usb_set_intfdata ( interface , adap ) ;
/* submit rx urb before enumerate clients */
ret = usb_submit_urb ( adap - > rx_urb , GFP_KERNEL ) ;
if ( ret ) {
dev_err ( dev , " submit rx urb failed: %d \n " , ret ) ;
goto err_free ;
}
ret = ljca_enumerate_clients ( adap ) ;
if ( ret )
goto err_free ;
usb_enable_autosuspend ( usb_dev ) ;
return 0 ;
err_free :
usb_free_urb ( adap - > rx_urb ) ;
err_put :
usb_put_intf ( adap - > intf ) ;
mutex_destroy ( & adap - > mutex ) ;
return ret ;
}
static void ljca_disconnect ( struct usb_interface * interface )
{
struct ljca_adapter * adap = usb_get_intfdata ( interface ) ;
struct ljca_client * client , * next ;
adap - > disconnect = true ;
usb_kill_urb ( adap - > rx_urb ) ;
list_for_each_entry_safe_reverse ( client , next , & adap - > client_list , link ) {
auxiliary_device_delete ( & client - > auxdev ) ;
auxiliary_device_uninit ( & client - > auxdev ) ;
list_del_init ( & client - > link ) ;
kfree ( client ) ;
}
usb_free_urb ( adap - > rx_urb ) ;
usb_put_intf ( adap - > intf ) ;
mutex_destroy ( & adap - > mutex ) ;
}
static int ljca_suspend ( struct usb_interface * interface , pm_message_t message )
{
struct ljca_adapter * adap = usb_get_intfdata ( interface ) ;
usb_kill_urb ( adap - > rx_urb ) ;
return 0 ;
}
static int ljca_resume ( struct usb_interface * interface )
{
struct ljca_adapter * adap = usb_get_intfdata ( interface ) ;
return usb_submit_urb ( adap - > rx_urb , GFP_KERNEL ) ;
}
static const struct usb_device_id ljca_table [ ] = {
{ USB_DEVICE ( 0x8086 , 0x0b63 ) } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( usb , ljca_table ) ;
static struct usb_driver ljca_driver = {
. name = " ljca " ,
. id_table = ljca_table ,
. probe = ljca_probe ,
. disconnect = ljca_disconnect ,
. suspend = ljca_suspend ,
. resume = ljca_resume ,
. supports_autosuspend = 1 ,
} ;
module_usb_driver ( ljca_driver ) ;
MODULE_AUTHOR ( " Wentong Wu <wentong.wu@intel.com> " ) ;
MODULE_AUTHOR ( " Zhifeng Wang <zhifeng.wang@intel.com> " ) ;
MODULE_AUTHOR ( " Lixu Zhang <lixu.zhang@intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel La Jolla Cove Adapter USB driver " ) ;
MODULE_LICENSE ( " GPL " ) ;