mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 07:05:12 +07:00
e99e88a9d2
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
312 lines
7.8 KiB
C
312 lines
7.8 KiB
C
/*
|
|
* Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip
|
|
*
|
|
* Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/sched.h>
|
|
#include <net/nfc/nci_core.h>
|
|
|
|
#include "st-nci.h"
|
|
|
|
#define NDLC_TIMER_T1 100
|
|
#define NDLC_TIMER_T1_WAIT 400
|
|
#define NDLC_TIMER_T2 1200
|
|
|
|
#define PCB_TYPE_DATAFRAME 0x80
|
|
#define PCB_TYPE_SUPERVISOR 0xc0
|
|
#define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR
|
|
|
|
#define PCB_SYNC_ACK 0x20
|
|
#define PCB_SYNC_NACK 0x10
|
|
#define PCB_SYNC_WAIT 0x30
|
|
#define PCB_SYNC_NOINFO 0x00
|
|
#define PCB_SYNC_MASK PCB_SYNC_WAIT
|
|
|
|
#define PCB_DATAFRAME_RETRANSMIT_YES 0x00
|
|
#define PCB_DATAFRAME_RETRANSMIT_NO 0x04
|
|
#define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO
|
|
|
|
#define PCB_SUPERVISOR_RETRANSMIT_YES 0x00
|
|
#define PCB_SUPERVISOR_RETRANSMIT_NO 0x02
|
|
#define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO
|
|
|
|
#define PCB_FRAME_CRC_INFO_PRESENT 0x08
|
|
#define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00
|
|
#define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT
|
|
|
|
#define NDLC_DUMP_SKB(info, skb) \
|
|
do { \
|
|
pr_debug("%s:\n", info); \
|
|
print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \
|
|
16, 1, skb->data, skb->len, 0); \
|
|
} while (0)
|
|
|
|
int ndlc_open(struct llt_ndlc *ndlc)
|
|
{
|
|
/* toggle reset pin */
|
|
ndlc->ops->enable(ndlc->phy_id);
|
|
ndlc->powered = 1;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ndlc_open);
|
|
|
|
void ndlc_close(struct llt_ndlc *ndlc)
|
|
{
|
|
struct nci_mode_set_cmd cmd;
|
|
|
|
cmd.cmd_type = ST_NCI_SET_NFC_MODE;
|
|
cmd.mode = 0;
|
|
|
|
/* toggle reset pin */
|
|
ndlc->ops->enable(ndlc->phy_id);
|
|
|
|
nci_prop_cmd(ndlc->ndev, ST_NCI_CORE_PROP,
|
|
sizeof(struct nci_mode_set_cmd), (__u8 *)&cmd);
|
|
|
|
ndlc->powered = 0;
|
|
ndlc->ops->disable(ndlc->phy_id);
|
|
}
|
|
EXPORT_SYMBOL(ndlc_close);
|
|
|
|
int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb)
|
|
{
|
|
/* add ndlc header */
|
|
u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO |
|
|
PCB_FRAME_CRC_INFO_NOTPRESENT;
|
|
|
|
*(u8 *)skb_push(skb, 1) = pcb;
|
|
skb_queue_tail(&ndlc->send_q, skb);
|
|
|
|
schedule_work(&ndlc->sm_work);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ndlc_send);
|
|
|
|
static void llt_ndlc_send_queue(struct llt_ndlc *ndlc)
|
|
{
|
|
struct sk_buff *skb;
|
|
int r;
|
|
unsigned long time_sent;
|
|
|
|
if (ndlc->send_q.qlen)
|
|
pr_debug("sendQlen=%d unackQlen=%d\n",
|
|
ndlc->send_q.qlen, ndlc->ack_pending_q.qlen);
|
|
|
|
while (ndlc->send_q.qlen) {
|
|
skb = skb_dequeue(&ndlc->send_q);
|
|
NDLC_DUMP_SKB("ndlc frame written", skb);
|
|
r = ndlc->ops->write(ndlc->phy_id, skb);
|
|
if (r < 0) {
|
|
ndlc->hard_fault = r;
|
|
break;
|
|
}
|
|
time_sent = jiffies;
|
|
*(unsigned long *)skb->cb = time_sent;
|
|
|
|
skb_queue_tail(&ndlc->ack_pending_q, skb);
|
|
|
|
/* start timer t1 for ndlc aknowledge */
|
|
ndlc->t1_active = true;
|
|
mod_timer(&ndlc->t1_timer, time_sent +
|
|
msecs_to_jiffies(NDLC_TIMER_T1));
|
|
/* start timer t2 for chip availability */
|
|
ndlc->t2_active = true;
|
|
mod_timer(&ndlc->t2_timer, time_sent +
|
|
msecs_to_jiffies(NDLC_TIMER_T2));
|
|
}
|
|
}
|
|
|
|
static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc)
|
|
{
|
|
struct sk_buff *skb;
|
|
u8 pcb;
|
|
|
|
while ((skb = skb_dequeue_tail(&ndlc->ack_pending_q))) {
|
|
pcb = skb->data[0];
|
|
switch (pcb & PCB_TYPE_MASK) {
|
|
case PCB_TYPE_SUPERVISOR:
|
|
skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) |
|
|
PCB_SUPERVISOR_RETRANSMIT_YES;
|
|
break;
|
|
case PCB_TYPE_DATAFRAME:
|
|
skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) |
|
|
PCB_DATAFRAME_RETRANSMIT_YES;
|
|
break;
|
|
default:
|
|
pr_err("UNKNOWN Packet Control Byte=%d\n", pcb);
|
|
kfree_skb(skb);
|
|
continue;
|
|
}
|
|
skb_queue_head(&ndlc->send_q, skb);
|
|
}
|
|
}
|
|
|
|
static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc)
|
|
{
|
|
struct sk_buff *skb;
|
|
u8 pcb;
|
|
unsigned long time_sent;
|
|
|
|
if (ndlc->rcv_q.qlen)
|
|
pr_debug("rcvQlen=%d\n", ndlc->rcv_q.qlen);
|
|
|
|
while ((skb = skb_dequeue(&ndlc->rcv_q)) != NULL) {
|
|
pcb = skb->data[0];
|
|
skb_pull(skb, 1);
|
|
if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) {
|
|
switch (pcb & PCB_SYNC_MASK) {
|
|
case PCB_SYNC_ACK:
|
|
skb = skb_dequeue(&ndlc->ack_pending_q);
|
|
kfree_skb(skb);
|
|
del_timer_sync(&ndlc->t1_timer);
|
|
del_timer_sync(&ndlc->t2_timer);
|
|
ndlc->t2_active = false;
|
|
ndlc->t1_active = false;
|
|
break;
|
|
case PCB_SYNC_NACK:
|
|
llt_ndlc_requeue_data_pending(ndlc);
|
|
llt_ndlc_send_queue(ndlc);
|
|
/* start timer t1 for ndlc aknowledge */
|
|
time_sent = jiffies;
|
|
ndlc->t1_active = true;
|
|
mod_timer(&ndlc->t1_timer, time_sent +
|
|
msecs_to_jiffies(NDLC_TIMER_T1));
|
|
break;
|
|
case PCB_SYNC_WAIT:
|
|
time_sent = jiffies;
|
|
ndlc->t1_active = true;
|
|
mod_timer(&ndlc->t1_timer, time_sent +
|
|
msecs_to_jiffies(NDLC_TIMER_T1_WAIT));
|
|
break;
|
|
default:
|
|
kfree_skb(skb);
|
|
break;
|
|
}
|
|
} else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) {
|
|
nci_recv_frame(ndlc->ndev, skb);
|
|
} else {
|
|
kfree_skb(skb);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void llt_ndlc_sm_work(struct work_struct *work)
|
|
{
|
|
struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work);
|
|
|
|
llt_ndlc_send_queue(ndlc);
|
|
llt_ndlc_rcv_queue(ndlc);
|
|
|
|
if (ndlc->t1_active && timer_pending(&ndlc->t1_timer) == 0) {
|
|
pr_debug
|
|
("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n");
|
|
ndlc->t1_active = false;
|
|
|
|
llt_ndlc_requeue_data_pending(ndlc);
|
|
llt_ndlc_send_queue(ndlc);
|
|
}
|
|
|
|
if (ndlc->t2_active && timer_pending(&ndlc->t2_timer) == 0) {
|
|
pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n");
|
|
ndlc->t2_active = false;
|
|
ndlc->t1_active = false;
|
|
del_timer_sync(&ndlc->t1_timer);
|
|
del_timer_sync(&ndlc->t2_timer);
|
|
ndlc_close(ndlc);
|
|
ndlc->hard_fault = -EREMOTEIO;
|
|
}
|
|
}
|
|
|
|
void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb)
|
|
{
|
|
if (skb == NULL) {
|
|
pr_err("NULL Frame -> link is dead\n");
|
|
ndlc->hard_fault = -EREMOTEIO;
|
|
ndlc_close(ndlc);
|
|
} else {
|
|
NDLC_DUMP_SKB("incoming frame", skb);
|
|
skb_queue_tail(&ndlc->rcv_q, skb);
|
|
}
|
|
|
|
schedule_work(&ndlc->sm_work);
|
|
}
|
|
EXPORT_SYMBOL(ndlc_recv);
|
|
|
|
static void ndlc_t1_timeout(struct timer_list *t)
|
|
{
|
|
struct llt_ndlc *ndlc = from_timer(ndlc, t, t1_timer);
|
|
|
|
pr_debug("\n");
|
|
|
|
schedule_work(&ndlc->sm_work);
|
|
}
|
|
|
|
static void ndlc_t2_timeout(struct timer_list *t)
|
|
{
|
|
struct llt_ndlc *ndlc = from_timer(ndlc, t, t2_timer);
|
|
|
|
pr_debug("\n");
|
|
|
|
schedule_work(&ndlc->sm_work);
|
|
}
|
|
|
|
int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
|
|
int phy_headroom, int phy_tailroom, struct llt_ndlc **ndlc_id,
|
|
struct st_nci_se_status *se_status)
|
|
{
|
|
struct llt_ndlc *ndlc;
|
|
|
|
ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
|
|
if (!ndlc)
|
|
return -ENOMEM;
|
|
|
|
ndlc->ops = phy_ops;
|
|
ndlc->phy_id = phy_id;
|
|
ndlc->dev = dev;
|
|
ndlc->powered = 0;
|
|
|
|
*ndlc_id = ndlc;
|
|
|
|
/* initialize timers */
|
|
timer_setup(&ndlc->t1_timer, ndlc_t1_timeout, 0);
|
|
timer_setup(&ndlc->t2_timer, ndlc_t2_timeout, 0);
|
|
|
|
skb_queue_head_init(&ndlc->rcv_q);
|
|
skb_queue_head_init(&ndlc->send_q);
|
|
skb_queue_head_init(&ndlc->ack_pending_q);
|
|
|
|
INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work);
|
|
|
|
return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status);
|
|
}
|
|
EXPORT_SYMBOL(ndlc_probe);
|
|
|
|
void ndlc_remove(struct llt_ndlc *ndlc)
|
|
{
|
|
st_nci_remove(ndlc->ndev);
|
|
|
|
/* cancel timers */
|
|
del_timer_sync(&ndlc->t1_timer);
|
|
del_timer_sync(&ndlc->t2_timer);
|
|
ndlc->t2_active = false;
|
|
ndlc->t1_active = false;
|
|
|
|
skb_queue_purge(&ndlc->rcv_q);
|
|
skb_queue_purge(&ndlc->send_q);
|
|
}
|
|
EXPORT_SYMBOL(ndlc_remove);
|