Merge branch 'qeth'
Merge s390 networking changes from Frank Blaschka. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -74,8 +74,8 @@ config QETH
|
|||||||
depends on CCW && NETDEVICES && IP_MULTICAST && QDIO
|
depends on CCW && NETDEVICES && IP_MULTICAST && QDIO
|
||||||
help
|
help
|
||||||
This driver supports the IBM System z OSA Express adapters
|
This driver supports the IBM System z OSA Express adapters
|
||||||
in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN
|
in QDIO mode (all media types), HiperSockets interfaces and z/VM
|
||||||
interfaces in QDIO and HIPER mode.
|
virtual NICs for Guest LAN and VSWITCH.
|
||||||
|
|
||||||
For details please refer to the documentation provided by IBM at
|
For details please refer to the documentation provided by IBM at
|
||||||
<http://www.ibm.com/developerworks/linux/linux390>
|
<http://www.ibm.com/developerworks/linux/linux390>
|
||||||
|
@@ -678,6 +678,7 @@ struct qeth_card_options {
|
|||||||
int performance_stats;
|
int performance_stats;
|
||||||
int rx_sg_cb;
|
int rx_sg_cb;
|
||||||
enum qeth_ipa_isolation_modes isolation;
|
enum qeth_ipa_isolation_modes isolation;
|
||||||
|
enum qeth_ipa_isolation_modes prev_isolation;
|
||||||
int sniffer;
|
int sniffer;
|
||||||
enum qeth_cq cq;
|
enum qeth_cq cq;
|
||||||
char hsuid[9];
|
char hsuid[9];
|
||||||
@@ -789,6 +790,7 @@ struct qeth_card {
|
|||||||
struct qeth_rx rx;
|
struct qeth_rx rx;
|
||||||
struct delayed_work buffer_reclaim_work;
|
struct delayed_work buffer_reclaim_work;
|
||||||
int reclaim_index;
|
int reclaim_index;
|
||||||
|
struct work_struct close_dev_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qeth_card_list_struct {
|
struct qeth_card_list_struct {
|
||||||
@@ -909,9 +911,6 @@ struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);
|
|||||||
int qeth_mdio_read(struct net_device *, int, int);
|
int qeth_mdio_read(struct net_device *, int, int);
|
||||||
int qeth_snmp_command(struct qeth_card *, char __user *);
|
int qeth_snmp_command(struct qeth_card *, char __user *);
|
||||||
int qeth_query_oat_command(struct qeth_card *, char __user *);
|
int qeth_query_oat_command(struct qeth_card *, char __user *);
|
||||||
struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *, __u32, __u32);
|
|
||||||
int qeth_default_setadapterparms_cb(struct qeth_card *, struct qeth_reply *,
|
|
||||||
unsigned long);
|
|
||||||
int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
|
int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *,
|
||||||
int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
|
int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long),
|
||||||
void *reply_param);
|
void *reply_param);
|
||||||
@@ -928,12 +927,13 @@ void qeth_core_get_strings(struct net_device *, u32, u8 *);
|
|||||||
void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
|
void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
|
||||||
void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...);
|
void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...);
|
||||||
int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
|
int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
|
||||||
int qeth_set_access_ctrl_online(struct qeth_card *card);
|
int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback);
|
||||||
int qeth_hdr_chk_and_bounce(struct sk_buff *, int);
|
int qeth_hdr_chk_and_bounce(struct sk_buff *, int);
|
||||||
int qeth_configure_cq(struct qeth_card *, enum qeth_cq);
|
int qeth_configure_cq(struct qeth_card *, enum qeth_cq);
|
||||||
int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
|
int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
|
||||||
int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
|
int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
|
||||||
void qeth_trace_features(struct qeth_card *);
|
void qeth_trace_features(struct qeth_card *);
|
||||||
|
void qeth_close_dev(struct qeth_card *);
|
||||||
|
|
||||||
/* exports for OSN */
|
/* exports for OSN */
|
||||||
int qeth_osn_assist(struct net_device *, void *, int);
|
int qeth_osn_assist(struct net_device *, void *, int);
|
||||||
|
@@ -68,6 +68,27 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
|
|||||||
enum qeth_qdio_buffer_states newbufstate);
|
enum qeth_qdio_buffer_states newbufstate);
|
||||||
static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
|
static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int);
|
||||||
|
|
||||||
|
static struct workqueue_struct *qeth_wq;
|
||||||
|
|
||||||
|
static void qeth_close_dev_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct qeth_card *card;
|
||||||
|
|
||||||
|
card = container_of(work, struct qeth_card, close_dev_work);
|
||||||
|
QETH_CARD_TEXT(card, 2, "cldevhdl");
|
||||||
|
rtnl_lock();
|
||||||
|
dev_close(card->dev);
|
||||||
|
rtnl_unlock();
|
||||||
|
ccwgroup_set_offline(card->gdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qeth_close_dev(struct qeth_card *card)
|
||||||
|
{
|
||||||
|
QETH_CARD_TEXT(card, 2, "cldevsubm");
|
||||||
|
queue_work(qeth_wq, &card->close_dev_work);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(qeth_close_dev);
|
||||||
|
|
||||||
static inline const char *qeth_get_cardname(struct qeth_card *card)
|
static inline const char *qeth_get_cardname(struct qeth_card *card)
|
||||||
{
|
{
|
||||||
if (card->info.guestlan) {
|
if (card->info.guestlan) {
|
||||||
@@ -542,11 +563,23 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
|
|||||||
} else {
|
} else {
|
||||||
switch (cmd->hdr.command) {
|
switch (cmd->hdr.command) {
|
||||||
case IPA_CMD_STOPLAN:
|
case IPA_CMD_STOPLAN:
|
||||||
dev_warn(&card->gdev->dev,
|
if (cmd->hdr.return_code ==
|
||||||
|
IPA_RC_VEPA_TO_VEB_TRANSITION) {
|
||||||
|
dev_err(&card->gdev->dev,
|
||||||
|
"Interface %s is down because the "
|
||||||
|
"adjacent port is no longer in "
|
||||||
|
"reflective relay mode\n",
|
||||||
|
QETH_CARD_IFNAME(card));
|
||||||
|
qeth_close_dev(card);
|
||||||
|
} else {
|
||||||
|
dev_warn(&card->gdev->dev,
|
||||||
"The link for interface %s on CHPID"
|
"The link for interface %s on CHPID"
|
||||||
" 0x%X failed\n",
|
" 0x%X failed\n",
|
||||||
QETH_CARD_IFNAME(card),
|
QETH_CARD_IFNAME(card),
|
||||||
card->info.chpid);
|
card->info.chpid);
|
||||||
|
qeth_issue_ipa_msg(cmd,
|
||||||
|
cmd->hdr.return_code, card);
|
||||||
|
}
|
||||||
card->lan_online = 0;
|
card->lan_online = 0;
|
||||||
if (card->dev && netif_carrier_ok(card->dev))
|
if (card->dev && netif_carrier_ok(card->dev))
|
||||||
netif_carrier_off(card->dev);
|
netif_carrier_off(card->dev);
|
||||||
@@ -1416,6 +1449,7 @@ static int qeth_setup_card(struct qeth_card *card)
|
|||||||
/* init QDIO stuff */
|
/* init QDIO stuff */
|
||||||
qeth_init_qdio_info(card);
|
qeth_init_qdio_info(card);
|
||||||
INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
|
INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
|
||||||
|
INIT_WORK(&card->close_dev_work, qeth_close_dev_handler);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2868,7 +2902,7 @@ int qeth_send_startlan(struct qeth_card *card)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_send_startlan);
|
EXPORT_SYMBOL_GPL(qeth_send_startlan);
|
||||||
|
|
||||||
int qeth_default_setadapterparms_cb(struct qeth_card *card,
|
static int qeth_default_setadapterparms_cb(struct qeth_card *card,
|
||||||
struct qeth_reply *reply, unsigned long data)
|
struct qeth_reply *reply, unsigned long data)
|
||||||
{
|
{
|
||||||
struct qeth_ipa_cmd *cmd;
|
struct qeth_ipa_cmd *cmd;
|
||||||
@@ -2881,7 +2915,6 @@ int qeth_default_setadapterparms_cb(struct qeth_card *card,
|
|||||||
cmd->data.setadapterparms.hdr.return_code;
|
cmd->data.setadapterparms.hdr.return_code;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_default_setadapterparms_cb);
|
|
||||||
|
|
||||||
static int qeth_query_setadapterparms_cb(struct qeth_card *card,
|
static int qeth_query_setadapterparms_cb(struct qeth_card *card,
|
||||||
struct qeth_reply *reply, unsigned long data)
|
struct qeth_reply *reply, unsigned long data)
|
||||||
@@ -2901,7 +2934,7 @@ static int qeth_query_setadapterparms_cb(struct qeth_card *card,
|
|||||||
return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
|
return qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
|
static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
|
||||||
__u32 command, __u32 cmdlen)
|
__u32 command, __u32 cmdlen)
|
||||||
{
|
{
|
||||||
struct qeth_cmd_buffer *iob;
|
struct qeth_cmd_buffer *iob;
|
||||||
@@ -2917,7 +2950,6 @@ struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card,
|
|||||||
|
|
||||||
return iob;
|
return iob;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(qeth_get_adapter_cmd);
|
|
||||||
|
|
||||||
int qeth_query_setadapterparms(struct qeth_card *card)
|
int qeth_query_setadapterparms(struct qeth_card *card)
|
||||||
{
|
{
|
||||||
@@ -4059,6 +4091,7 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
|
|||||||
{
|
{
|
||||||
struct qeth_ipa_cmd *cmd;
|
struct qeth_ipa_cmd *cmd;
|
||||||
struct qeth_set_access_ctrl *access_ctrl_req;
|
struct qeth_set_access_ctrl *access_ctrl_req;
|
||||||
|
int fallback = *(int *)reply->param;
|
||||||
|
|
||||||
QETH_CARD_TEXT(card, 4, "setaccb");
|
QETH_CARD_TEXT(card, 4, "setaccb");
|
||||||
|
|
||||||
@@ -4068,12 +4101,14 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
|
|||||||
QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
|
QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "rc=%d",
|
QETH_DBF_TEXT_(SETUP, 2, "rc=%d",
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
cmd->data.setadapterparms.hdr.return_code);
|
||||||
|
if (cmd->data.setadapterparms.hdr.return_code !=
|
||||||
|
SET_ACCESS_CTRL_RC_SUCCESS)
|
||||||
|
QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n",
|
||||||
|
card->gdev->dev.kobj.name,
|
||||||
|
access_ctrl_req->subcmd_code,
|
||||||
|
cmd->data.setadapterparms.hdr.return_code);
|
||||||
switch (cmd->data.setadapterparms.hdr.return_code) {
|
switch (cmd->data.setadapterparms.hdr.return_code) {
|
||||||
case SET_ACCESS_CTRL_RC_SUCCESS:
|
case SET_ACCESS_CTRL_RC_SUCCESS:
|
||||||
case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED:
|
|
||||||
case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED:
|
|
||||||
{
|
|
||||||
card->options.isolation = access_ctrl_req->subcmd_code;
|
|
||||||
if (card->options.isolation == ISOLATION_MODE_NONE) {
|
if (card->options.isolation == ISOLATION_MODE_NONE) {
|
||||||
dev_info(&card->gdev->dev,
|
dev_info(&card->gdev->dev,
|
||||||
"QDIO data connection isolation is deactivated\n");
|
"QDIO data connection isolation is deactivated\n");
|
||||||
@@ -4081,72 +4116,64 @@ static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
|
|||||||
dev_info(&card->gdev->dev,
|
dev_info(&card->gdev->dev,
|
||||||
"QDIO data connection isolation is activated\n");
|
"QDIO data connection isolation is activated\n");
|
||||||
}
|
}
|
||||||
QETH_DBF_MESSAGE(3, "OK:SET_ACCESS_CTRL(%s, %d)==%d\n",
|
|
||||||
card->gdev->dev.kobj.name,
|
|
||||||
access_ctrl_req->subcmd_code,
|
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
|
||||||
break;
|
break;
|
||||||
}
|
case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED:
|
||||||
|
QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already "
|
||||||
|
"deactivated\n", dev_name(&card->gdev->dev));
|
||||||
|
if (fallback)
|
||||||
|
card->options.isolation = card->options.prev_isolation;
|
||||||
|
break;
|
||||||
|
case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED:
|
||||||
|
QETH_DBF_MESSAGE(2, "%s QDIO data connection isolation already"
|
||||||
|
" activated\n", dev_name(&card->gdev->dev));
|
||||||
|
if (fallback)
|
||||||
|
card->options.isolation = card->options.prev_isolation;
|
||||||
|
break;
|
||||||
case SET_ACCESS_CTRL_RC_NOT_SUPPORTED:
|
case SET_ACCESS_CTRL_RC_NOT_SUPPORTED:
|
||||||
{
|
|
||||||
QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n",
|
|
||||||
card->gdev->dev.kobj.name,
|
|
||||||
access_ctrl_req->subcmd_code,
|
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
|
||||||
dev_err(&card->gdev->dev, "Adapter does not "
|
dev_err(&card->gdev->dev, "Adapter does not "
|
||||||
"support QDIO data connection isolation\n");
|
"support QDIO data connection isolation\n");
|
||||||
|
|
||||||
/* ensure isolation mode is "none" */
|
|
||||||
card->options.isolation = ISOLATION_MODE_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER:
|
case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER:
|
||||||
{
|
|
||||||
QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n",
|
|
||||||
card->gdev->dev.kobj.name,
|
|
||||||
access_ctrl_req->subcmd_code,
|
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
|
||||||
dev_err(&card->gdev->dev,
|
dev_err(&card->gdev->dev,
|
||||||
"Adapter is dedicated. "
|
"Adapter is dedicated. "
|
||||||
"QDIO data connection isolation not supported\n");
|
"QDIO data connection isolation not supported\n");
|
||||||
|
if (fallback)
|
||||||
/* ensure isolation mode is "none" */
|
card->options.isolation = card->options.prev_isolation;
|
||||||
card->options.isolation = ISOLATION_MODE_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF:
|
case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF:
|
||||||
{
|
|
||||||
QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n",
|
|
||||||
card->gdev->dev.kobj.name,
|
|
||||||
access_ctrl_req->subcmd_code,
|
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
|
||||||
dev_err(&card->gdev->dev,
|
dev_err(&card->gdev->dev,
|
||||||
"TSO does not permit QDIO data connection isolation\n");
|
"TSO does not permit QDIO data connection isolation\n");
|
||||||
|
if (fallback)
|
||||||
/* ensure isolation mode is "none" */
|
card->options.isolation = card->options.prev_isolation;
|
||||||
card->options.isolation = ISOLATION_MODE_NONE;
|
break;
|
||||||
|
case SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED:
|
||||||
|
dev_err(&card->gdev->dev, "The adjacent switch port does not "
|
||||||
|
"support reflective relay mode\n");
|
||||||
|
if (fallback)
|
||||||
|
card->options.isolation = card->options.prev_isolation;
|
||||||
|
break;
|
||||||
|
case SET_ACCESS_CTRL_RC_REFLREL_FAILED:
|
||||||
|
dev_err(&card->gdev->dev, "The reflective relay mode cannot be "
|
||||||
|
"enabled at the adjacent switch port");
|
||||||
|
if (fallback)
|
||||||
|
card->options.isolation = card->options.prev_isolation;
|
||||||
|
break;
|
||||||
|
case SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED:
|
||||||
|
dev_warn(&card->gdev->dev, "Turning off reflective relay mode "
|
||||||
|
"at the adjacent switch failed\n");
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
{
|
|
||||||
/* this should never happen */
|
/* this should never happen */
|
||||||
QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d"
|
if (fallback)
|
||||||
"==UNKNOWN\n",
|
card->options.isolation = card->options.prev_isolation;
|
||||||
card->gdev->dev.kobj.name,
|
|
||||||
access_ctrl_req->subcmd_code,
|
|
||||||
cmd->data.setadapterparms.hdr.return_code);
|
|
||||||
|
|
||||||
/* ensure isolation mode is "none" */
|
|
||||||
card->options.isolation = ISOLATION_MODE_NONE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
|
qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
|
static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
|
||||||
enum qeth_ipa_isolation_modes isolation)
|
enum qeth_ipa_isolation_modes isolation, int fallback)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct qeth_cmd_buffer *iob;
|
struct qeth_cmd_buffer *iob;
|
||||||
@@ -4166,12 +4193,12 @@ static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
|
|||||||
access_ctrl_req->subcmd_code = isolation;
|
access_ctrl_req->subcmd_code = isolation;
|
||||||
|
|
||||||
rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb,
|
rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb,
|
||||||
NULL);
|
&fallback);
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qeth_set_access_ctrl_online(struct qeth_card *card)
|
int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
@@ -4181,12 +4208,13 @@ int qeth_set_access_ctrl_online(struct qeth_card *card)
|
|||||||
card->info.type == QETH_CARD_TYPE_OSX) &&
|
card->info.type == QETH_CARD_TYPE_OSX) &&
|
||||||
qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) {
|
qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) {
|
||||||
rc = qeth_setadpparms_set_access_ctrl(card,
|
rc = qeth_setadpparms_set_access_ctrl(card,
|
||||||
card->options.isolation);
|
card->options.isolation, fallback);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
QETH_DBF_MESSAGE(3,
|
QETH_DBF_MESSAGE(3,
|
||||||
"IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n",
|
"IPA(SET_ACCESS_CTRL,%s,%d) sent failed\n",
|
||||||
card->gdev->dev.kobj.name,
|
card->gdev->dev.kobj.name,
|
||||||
rc);
|
rc);
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
} else if (card->options.isolation != ISOLATION_MODE_NONE) {
|
} else if (card->options.isolation != ISOLATION_MODE_NONE) {
|
||||||
card->options.isolation = ISOLATION_MODE_NONE;
|
card->options.isolation = ISOLATION_MODE_NONE;
|
||||||
@@ -4672,7 +4700,7 @@ static int qeth_qdio_establish(struct qeth_card *card)
|
|||||||
init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
|
init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
|
||||||
init_data.output_sbal_state_array = card->qdio.out_bufstates;
|
init_data.output_sbal_state_array = card->qdio.out_bufstates;
|
||||||
init_data.scan_threshold =
|
init_data.scan_threshold =
|
||||||
(card->info.type == QETH_CARD_TYPE_IQD) ? 8 : 32;
|
(card->info.type == QETH_CARD_TYPE_IQD) ? 1 : 32;
|
||||||
|
|
||||||
if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
|
if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED,
|
||||||
QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) {
|
QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) {
|
||||||
@@ -4765,14 +4793,14 @@ static struct ccw_driver qeth_ccw_driver = {
|
|||||||
|
|
||||||
int qeth_core_hardsetup_card(struct qeth_card *card)
|
int qeth_core_hardsetup_card(struct qeth_card *card)
|
||||||
{
|
{
|
||||||
int retries = 0;
|
int retries = 3;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
|
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
|
||||||
atomic_set(&card->force_alloc_skb, 0);
|
atomic_set(&card->force_alloc_skb, 0);
|
||||||
qeth_update_from_chp_desc(card);
|
qeth_update_from_chp_desc(card);
|
||||||
retry:
|
retry:
|
||||||
if (retries)
|
if (retries < 3)
|
||||||
QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
|
QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
|
||||||
dev_name(&card->gdev->dev));
|
dev_name(&card->gdev->dev));
|
||||||
ccw_device_set_offline(CARD_DDEV(card));
|
ccw_device_set_offline(CARD_DDEV(card));
|
||||||
@@ -4794,7 +4822,7 @@ retriable:
|
|||||||
return rc;
|
return rc;
|
||||||
} else if (rc) {
|
} else if (rc) {
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
|
||||||
if (++retries > 3)
|
if (--retries < 0)
|
||||||
goto out;
|
goto out;
|
||||||
else
|
else
|
||||||
goto retry;
|
goto retry;
|
||||||
@@ -5094,13 +5122,81 @@ static const struct device_type qeth_osn_devtype = {
|
|||||||
.groups = qeth_osn_attr_groups,
|
.groups = qeth_osn_attr_groups,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DBF_NAME_LEN 20
|
||||||
|
|
||||||
|
struct qeth_dbf_entry {
|
||||||
|
char dbf_name[DBF_NAME_LEN];
|
||||||
|
debug_info_t *dbf_info;
|
||||||
|
struct list_head dbf_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(qeth_dbf_list);
|
||||||
|
static DEFINE_MUTEX(qeth_dbf_list_mutex);
|
||||||
|
|
||||||
|
static debug_info_t *qeth_get_dbf_entry(char *name)
|
||||||
|
{
|
||||||
|
struct qeth_dbf_entry *entry;
|
||||||
|
debug_info_t *rc = NULL;
|
||||||
|
|
||||||
|
mutex_lock(&qeth_dbf_list_mutex);
|
||||||
|
list_for_each_entry(entry, &qeth_dbf_list, dbf_list) {
|
||||||
|
if (strcmp(entry->dbf_name, name) == 0) {
|
||||||
|
rc = entry->dbf_info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&qeth_dbf_list_mutex);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qeth_add_dbf_entry(struct qeth_card *card, char *name)
|
||||||
|
{
|
||||||
|
struct qeth_dbf_entry *new_entry;
|
||||||
|
|
||||||
|
card->debug = debug_register(name, 2, 1, 8);
|
||||||
|
if (!card->debug) {
|
||||||
|
QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (debug_register_view(card->debug, &debug_hex_ascii_view))
|
||||||
|
goto err_dbg;
|
||||||
|
new_entry = kzalloc(sizeof(struct qeth_dbf_entry), GFP_KERNEL);
|
||||||
|
if (!new_entry)
|
||||||
|
goto err_dbg;
|
||||||
|
strncpy(new_entry->dbf_name, name, DBF_NAME_LEN);
|
||||||
|
new_entry->dbf_info = card->debug;
|
||||||
|
mutex_lock(&qeth_dbf_list_mutex);
|
||||||
|
list_add(&new_entry->dbf_list, &qeth_dbf_list);
|
||||||
|
mutex_unlock(&qeth_dbf_list_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_dbg:
|
||||||
|
debug_unregister(card->debug);
|
||||||
|
err:
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qeth_clear_dbf_list(void)
|
||||||
|
{
|
||||||
|
struct qeth_dbf_entry *entry, *tmp;
|
||||||
|
|
||||||
|
mutex_lock(&qeth_dbf_list_mutex);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &qeth_dbf_list, dbf_list) {
|
||||||
|
list_del(&entry->dbf_list);
|
||||||
|
debug_unregister(entry->dbf_info);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
mutex_unlock(&qeth_dbf_list_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
||||||
{
|
{
|
||||||
struct qeth_card *card;
|
struct qeth_card *card;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
int rc;
|
int rc;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
char dbf_name[20];
|
char dbf_name[DBF_NAME_LEN];
|
||||||
|
|
||||||
QETH_DBF_TEXT(SETUP, 2, "probedev");
|
QETH_DBF_TEXT(SETUP, 2, "probedev");
|
||||||
|
|
||||||
@@ -5119,13 +5215,12 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
|||||||
|
|
||||||
snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s",
|
snprintf(dbf_name, sizeof(dbf_name), "qeth_card_%s",
|
||||||
dev_name(&gdev->dev));
|
dev_name(&gdev->dev));
|
||||||
card->debug = debug_register(dbf_name, 2, 1, 8);
|
card->debug = qeth_get_dbf_entry(dbf_name);
|
||||||
if (!card->debug) {
|
if (!card->debug) {
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "%s", "qcdbf");
|
rc = qeth_add_dbf_entry(card, dbf_name);
|
||||||
rc = -ENOMEM;
|
if (rc)
|
||||||
goto err_card;
|
goto err_card;
|
||||||
}
|
}
|
||||||
debug_register_view(card->debug, &debug_hex_ascii_view);
|
|
||||||
|
|
||||||
card->read.ccwdev = gdev->cdev[0];
|
card->read.ccwdev = gdev->cdev[0];
|
||||||
card->write.ccwdev = gdev->cdev[1];
|
card->write.ccwdev = gdev->cdev[1];
|
||||||
@@ -5139,12 +5234,12 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
|||||||
rc = qeth_determine_card_type(card);
|
rc = qeth_determine_card_type(card);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
||||||
goto err_dbf;
|
goto err_card;
|
||||||
}
|
}
|
||||||
rc = qeth_setup_card(card);
|
rc = qeth_setup_card(card);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
||||||
goto err_dbf;
|
goto err_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card->info.type == QETH_CARD_TYPE_OSN)
|
if (card->info.type == QETH_CARD_TYPE_OSN)
|
||||||
@@ -5157,7 +5252,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
|||||||
case QETH_CARD_TYPE_OSM:
|
case QETH_CARD_TYPE_OSM:
|
||||||
rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);
|
rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto err_dbf;
|
goto err_card;
|
||||||
rc = card->discipline->setup(card->gdev);
|
rc = card->discipline->setup(card->gdev);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto err_disc;
|
goto err_disc;
|
||||||
@@ -5176,8 +5271,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
|||||||
|
|
||||||
err_disc:
|
err_disc:
|
||||||
qeth_core_free_discipline(card);
|
qeth_core_free_discipline(card);
|
||||||
err_dbf:
|
|
||||||
debug_unregister(card->debug);
|
|
||||||
err_card:
|
err_card:
|
||||||
qeth_core_free_card(card);
|
qeth_core_free_card(card);
|
||||||
err_dev:
|
err_dev:
|
||||||
@@ -5197,7 +5290,6 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev)
|
|||||||
qeth_core_free_discipline(card);
|
qeth_core_free_discipline(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_unregister(card->debug);
|
|
||||||
write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
|
write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
|
||||||
list_del(&card->list);
|
list_del(&card->list);
|
||||||
write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
|
write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
|
||||||
@@ -5551,9 +5643,12 @@ static int __init qeth_core_init(void)
|
|||||||
|
|
||||||
pr_info("loading core functions\n");
|
pr_info("loading core functions\n");
|
||||||
INIT_LIST_HEAD(&qeth_core_card_list.list);
|
INIT_LIST_HEAD(&qeth_core_card_list.list);
|
||||||
|
INIT_LIST_HEAD(&qeth_dbf_list);
|
||||||
rwlock_init(&qeth_core_card_list.rwlock);
|
rwlock_init(&qeth_core_card_list.rwlock);
|
||||||
mutex_init(&qeth_mod_mutex);
|
mutex_init(&qeth_mod_mutex);
|
||||||
|
|
||||||
|
qeth_wq = create_singlethread_workqueue("qeth_wq");
|
||||||
|
|
||||||
rc = qeth_register_dbf_views();
|
rc = qeth_register_dbf_views();
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
@@ -5600,6 +5695,8 @@ out_err:
|
|||||||
|
|
||||||
static void __exit qeth_core_exit(void)
|
static void __exit qeth_core_exit(void)
|
||||||
{
|
{
|
||||||
|
qeth_clear_dbf_list();
|
||||||
|
destroy_workqueue(qeth_wq);
|
||||||
ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
|
ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
|
||||||
ccw_driver_unregister(&qeth_ccw_driver);
|
ccw_driver_unregister(&qeth_ccw_driver);
|
||||||
kmem_cache_destroy(qeth_qdio_outbuf_cache);
|
kmem_cache_destroy(qeth_qdio_outbuf_cache);
|
||||||
|
@@ -204,6 +204,7 @@ static struct ipa_rc_msg qeth_ipa_rc_msg[] = {
|
|||||||
{IPA_RC_INVALID_SETRTG_INDICATOR, "Invalid SETRTG indicator"},
|
{IPA_RC_INVALID_SETRTG_INDICATOR, "Invalid SETRTG indicator"},
|
||||||
{IPA_RC_MC_ADDR_ALREADY_DEFINED, "Multicast address already defined"},
|
{IPA_RC_MC_ADDR_ALREADY_DEFINED, "Multicast address already defined"},
|
||||||
{IPA_RC_LAN_OFFLINE, "STRTLAN_LAN_DISABLED - LAN offline"},
|
{IPA_RC_LAN_OFFLINE, "STRTLAN_LAN_DISABLED - LAN offline"},
|
||||||
|
{IPA_RC_VEPA_TO_VEB_TRANSITION, "Adj. switch disabled port mode RR"},
|
||||||
{IPA_RC_INVALID_IP_VERSION2, "Invalid IP version"},
|
{IPA_RC_INVALID_IP_VERSION2, "Invalid IP version"},
|
||||||
{IPA_RC_ENOMEM, "Memory problem"},
|
{IPA_RC_ENOMEM, "Memory problem"},
|
||||||
{IPA_RC_FFFF, "Unknown Error"}
|
{IPA_RC_FFFF, "Unknown Error"}
|
||||||
|
@@ -177,6 +177,7 @@ enum qeth_ipa_return_codes {
|
|||||||
IPA_RC_INVALID_SETRTG_INDICATOR = 0xe012,
|
IPA_RC_INVALID_SETRTG_INDICATOR = 0xe012,
|
||||||
IPA_RC_MC_ADDR_ALREADY_DEFINED = 0xe013,
|
IPA_RC_MC_ADDR_ALREADY_DEFINED = 0xe013,
|
||||||
IPA_RC_LAN_OFFLINE = 0xe080,
|
IPA_RC_LAN_OFFLINE = 0xe080,
|
||||||
|
IPA_RC_VEPA_TO_VEB_TRANSITION = 0xe090,
|
||||||
IPA_RC_INVALID_IP_VERSION2 = 0xf001,
|
IPA_RC_INVALID_IP_VERSION2 = 0xf001,
|
||||||
IPA_RC_ENOMEM = 0xfffe,
|
IPA_RC_ENOMEM = 0xfffe,
|
||||||
IPA_RC_FFFF = 0xffff
|
IPA_RC_FFFF = 0xffff
|
||||||
@@ -269,6 +270,9 @@ enum qeth_ipa_set_access_mode_rc {
|
|||||||
SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010,
|
SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010,
|
||||||
SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014,
|
SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014,
|
||||||
SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018,
|
SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018,
|
||||||
|
SET_ACCESS_CTRL_RC_REFLREL_UNSUPPORTED = 0x0022,
|
||||||
|
SET_ACCESS_CTRL_RC_REFLREL_FAILED = 0x0024,
|
||||||
|
SET_ACCESS_CTRL_RC_REFLREL_DEACT_FAILED = 0x0028,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -386,6 +390,7 @@ struct qeth_snmp_ureq {
|
|||||||
/* SET_ACCESS_CONTROL: same format for request and reply */
|
/* SET_ACCESS_CONTROL: same format for request and reply */
|
||||||
struct qeth_set_access_ctrl {
|
struct qeth_set_access_ctrl {
|
||||||
__u32 subcmd_code;
|
__u32 subcmd_code;
|
||||||
|
__u8 reserved[8];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct qeth_query_oat {
|
struct qeth_query_oat {
|
||||||
|
@@ -513,10 +513,11 @@ static ssize_t qeth_dev_isolation_store(struct device *dev,
|
|||||||
rc = count;
|
rc = count;
|
||||||
|
|
||||||
/* defer IP assist if device is offline (until discipline->set_online)*/
|
/* defer IP assist if device is offline (until discipline->set_online)*/
|
||||||
|
card->options.prev_isolation = card->options.isolation;
|
||||||
card->options.isolation = isolation;
|
card->options.isolation = isolation;
|
||||||
if (card->state == CARD_STATE_SOFTSETUP ||
|
if (card->state == CARD_STATE_SOFTSETUP ||
|
||||||
card->state == CARD_STATE_UP) {
|
card->state == CARD_STATE_UP) {
|
||||||
int ipa_rc = qeth_set_access_ctrl_online(card);
|
int ipa_rc = qeth_set_access_ctrl_online(card, 1);
|
||||||
if (ipa_rc != 0)
|
if (ipa_rc != 0)
|
||||||
rc = ipa_rc;
|
rc = ipa_rc;
|
||||||
}
|
}
|
||||||
|
@@ -1025,9 +1025,14 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
|||||||
|
|
||||||
contin:
|
contin:
|
||||||
if ((card->info.type == QETH_CARD_TYPE_OSD) ||
|
if ((card->info.type == QETH_CARD_TYPE_OSD) ||
|
||||||
(card->info.type == QETH_CARD_TYPE_OSX))
|
(card->info.type == QETH_CARD_TYPE_OSX)) {
|
||||||
/* configure isolation level */
|
/* configure isolation level */
|
||||||
qeth_set_access_ctrl_online(card);
|
rc = qeth_set_access_ctrl_online(card, 0);
|
||||||
|
if (rc) {
|
||||||
|
rc = -ENODEV;
|
||||||
|
goto out_remove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (card->info.type != QETH_CARD_TYPE_OSN &&
|
if (card->info.type != QETH_CARD_TYPE_OSN &&
|
||||||
card->info.type != QETH_CARD_TYPE_OSM)
|
card->info.type != QETH_CARD_TYPE_OSM)
|
||||||
@@ -1144,12 +1149,9 @@ static int qeth_l2_recover(void *ptr)
|
|||||||
dev_info(&card->gdev->dev,
|
dev_info(&card->gdev->dev,
|
||||||
"Device successfully recovered!\n");
|
"Device successfully recovered!\n");
|
||||||
else {
|
else {
|
||||||
if (rtnl_trylock()) {
|
qeth_close_dev(card);
|
||||||
dev_close(card->dev);
|
dev_warn(&card->gdev->dev, "The qeth device driver "
|
||||||
rtnl_unlock();
|
|
||||||
dev_warn(&card->gdev->dev, "The qeth device driver "
|
|
||||||
"failed to recover an error on the device\n");
|
"failed to recover an error on the device\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
|
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
|
||||||
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
|
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
|
||||||
|
@@ -1449,7 +1449,8 @@ static int qeth_l3_start_ipassists(struct qeth_card *card)
|
|||||||
{
|
{
|
||||||
QETH_CARD_TEXT(card, 3, "strtipas");
|
QETH_CARD_TEXT(card, 3, "strtipas");
|
||||||
|
|
||||||
qeth_set_access_ctrl_online(card); /* go on*/
|
if (qeth_set_access_ctrl_online(card, 0))
|
||||||
|
return -EIO;
|
||||||
qeth_l3_start_ipa_arp_processing(card); /* go on*/
|
qeth_l3_start_ipa_arp_processing(card); /* go on*/
|
||||||
qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/
|
qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/
|
||||||
qeth_l3_start_ipa_source_mac(card); /* go on*/
|
qeth_l3_start_ipa_source_mac(card); /* go on*/
|
||||||
@@ -3388,8 +3389,10 @@ contin:
|
|||||||
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
||||||
if (!card->options.sniffer) {
|
if (!card->options.sniffer) {
|
||||||
rc = qeth_l3_start_ipassists(card);
|
rc = qeth_l3_start_ipassists(card);
|
||||||
if (rc)
|
if (rc) {
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
||||||
|
goto out_remove;
|
||||||
|
}
|
||||||
rc = qeth_l3_setrouting_v4(card);
|
rc = qeth_l3_setrouting_v4(card);
|
||||||
if (rc)
|
if (rc)
|
||||||
QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
|
QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
|
||||||
@@ -3511,12 +3514,9 @@ static int qeth_l3_recover(void *ptr)
|
|||||||
dev_info(&card->gdev->dev,
|
dev_info(&card->gdev->dev,
|
||||||
"Device successfully recovered!\n");
|
"Device successfully recovered!\n");
|
||||||
else {
|
else {
|
||||||
if (rtnl_trylock()) {
|
qeth_close_dev(card);
|
||||||
dev_close(card->dev);
|
dev_warn(&card->gdev->dev, "The qeth device driver "
|
||||||
rtnl_unlock();
|
|
||||||
dev_warn(&card->gdev->dev, "The qeth device driver "
|
|
||||||
"failed to recover an error on the device\n");
|
"failed to recover an error on the device\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
|
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
|
||||||
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
|
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
|
||||||
|
Reference in New Issue
Block a user