* net/dccp/ccids/ccid3.c
*
* Copyright (c) 2005 The University of Waikato, Hamilton, New Zealand.
- * Copyright (c) 2005-6 Ian McDonald <imcdnzl@gmail.com>
+ * Copyright (c) 2005-6 Ian McDonald <ian.mcdonald@jandi.co.nz>
*
* An implementation of the DCCP protocol
*
return (b >= 2 * div) ? tmp / (b / div) : tmp;
}
-static int ccid3_debug;
-#ifdef CCID3_DEBUG
-#define ccid3_pr_debug(format, a...) \
- do { if (ccid3_debug) \
- printk(KERN_DEBUG "%s: " format, __FUNCTION__, ##a); \
- } while (0)
+
+#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
+static int ccid3_debug;
+#define ccid3_pr_debug(format, a...) DCCP_PR_DEBUG(ccid3_debug, format, ##a)
#else
#define ccid3_pr_debug(format, a...)
#endif
static struct dccp_rx_hist *ccid3_rx_hist;
static struct dccp_li_hist *ccid3_li_hist;
-/* TFRC sender states */
-enum ccid3_hc_tx_states {
- TFRC_SSTATE_NO_SENT = 1,
- TFRC_SSTATE_NO_FBACK,
- TFRC_SSTATE_FBACK,
- TFRC_SSTATE_TERM,
-};
-
-#ifdef CCID3_DEBUG
+#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state)
{
static char *ccid3_state_names[] = {
ccid3_tx_state_name(hctx->ccid3hctx_state));
switch (hctx->ccid3hctx_state) {
- case TFRC_SSTATE_TERM:
- goto out;
case TFRC_SSTATE_NO_FBACK:
/* Halve send rate */
hctx->ccid3hctx_x /= 2;
2 * usecs_div(hctx->ccid3hctx_s,
hctx->ccid3hctx_x));
break;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hctx->ccid3hctx_state);
- dump_stack();
+ case TFRC_SSTATE_NO_SENT:
+ DCCP_BUG("Illegal %s state NO_SENT, sk=%p", dccp_role(sk), sk);
+ /* fall through */
+ case TFRC_SSTATE_TERM:
goto out;
}
long delay;
int rc = -ENOTCONN;
- BUG_ON(hctx == NULL || hctx->ccid3hctx_state == TFRC_SSTATE_TERM);
+ BUG_ON(hctx == NULL);
/* Check if pure ACK or Terminating*/
/*
rc = -ENOBUFS;
if (unlikely(new_packet == NULL)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, not enough "
- "mem to add to history, send refused\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, not enough mem to add to history,"
+ "send refused\n", dccp_role(sk), sk);
goto out;
}
/* divide by -1000 is to convert to ms and get sign right */
rc = delay > 0 ? delay : 0;
break;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hctx->ccid3hctx_state);
- dump_stack();
+ case TFRC_SSTATE_TERM:
+ DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
rc = -EINVAL;
break;
}
new_packet->dccphtx_ccval =
DCCP_SKB_CB(skb)->dccpd_ccval =
hctx->ccid3hctx_last_win_count;
+ timeval_add_usecs(&hctx->ccid3hctx_t_nom,
+ hctx->ccid3hctx_t_ipi);
}
out:
return rc;
struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
struct timeval now;
- BUG_ON(hctx == NULL || hctx->ccid3hctx_state == TFRC_SSTATE_TERM);
+ BUG_ON(hctx == NULL);
dccp_timestamp(sk, &now);
packet = dccp_tx_hist_head(&hctx->ccid3hctx_hist);
if (unlikely(packet == NULL)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: packet doesn't "
- "exists in history!\n", __FUNCTION__);
+ DCCP_WARN("packet doesn't exist in history!\n");
return;
}
if (unlikely(packet->dccphtx_sent)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: no unsent packet in "
- "history!\n", __FUNCTION__);
+ DCCP_WARN("no unsent packet in history!\n");
return;
}
packet->dccphtx_tstamp = now;
packet->dccphtx_seqno = dp->dccps_gss;
/*
* Check if win_count have changed
- * Algorithm in "8.1. Window Counter Valuer" in
- * draft-ietf-dccp-ccid3-11.txt
+ * Algorithm in "8.1. Window Counter Value" in RFC 4342.
*/
quarter_rtt = timeval_delta(&now, &hctx->ccid3hctx_t_last_win_count);
if (likely(hctx->ccid3hctx_rtt > 8))
case TFRC_SSTATE_NO_SENT:
/* if first wasn't pure ack */
if (len != 0)
- printk(KERN_CRIT "%s: %s, First packet sent is noted "
- "as a data packet\n",
- __FUNCTION__, dccp_role(sk));
+ DCCP_CRIT("%s, First packet sent is noted "
+ "as a data packet", dccp_role(sk));
return;
case TFRC_SSTATE_NO_FBACK:
case TFRC_SSTATE_FBACK:
if (len > 0) {
- hctx->ccid3hctx_t_nom = now;
+ timeval_sub_usecs(&hctx->ccid3hctx_t_nom,
+ hctx->ccid3hctx_t_ipi);
ccid3_calc_new_t_ipi(hctx);
ccid3_calc_new_delta(hctx);
timeval_add_usecs(&hctx->ccid3hctx_t_nom,
hctx->ccid3hctx_t_ipi);
}
break;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hctx->ccid3hctx_state);
- dump_stack();
+ case TFRC_SSTATE_TERM:
+ DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
break;
}
}
u32 x_recv;
u32 r_sample;
- BUG_ON(hctx == NULL || hctx->ccid3hctx_state == TFRC_SSTATE_TERM);
+ BUG_ON(hctx == NULL);
/* we are only interested in ACKs */
if (!(DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_ACK ||
packet = dccp_tx_hist_find_entry(&hctx->ccid3hctx_hist,
DCCP_SKB_CB(skb)->dccpd_ack_seq);
if (unlikely(packet == NULL)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, seqno "
- "%llu(%s) does't exist in history!\n",
- __FUNCTION__, dccp_role(sk), sk,
+ DCCP_WARN("%s, sk=%p, seqno %llu(%s) does't exist "
+ "in history!\n", dccp_role(sk), sk,
(unsigned long long)DCCP_SKB_CB(skb)->dccpd_ack_seq,
- dccp_packet_name(DCCP_SKB_CB(skb)->dccpd_type));
+ dccp_packet_name(DCCP_SKB_CB(skb)->dccpd_type));
return;
}
dccp_timestamp(sk, &now);
r_sample = timeval_delta(&now, &packet->dccphtx_tstamp);
if (unlikely(r_sample <= t_elapsed))
- LIMIT_NETDEBUG(KERN_WARNING "%s: r_sample=%uus, "
- "t_elapsed=%uus\n",
- __FUNCTION__, r_sample, t_elapsed);
+ DCCP_WARN("r_sample=%uus,t_elapsed=%uus\n",
+ r_sample, t_elapsed);
else
r_sample -= t_elapsed;
/* set idle flag */
hctx->ccid3hctx_idle = 1;
break;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hctx->ccid3hctx_state);
- dump_stack();
+ case TFRC_SSTATE_TERM:
+ DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
break;
}
}
switch (option) {
case TFRC_OPT_LOSS_EVENT_RATE:
if (unlikely(len != 4)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, invalid "
- "len for TFRC_OPT_LOSS_EVENT_RATE\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, invalid len %d "
+ "for TFRC_OPT_LOSS_EVENT_RATE\n",
+ dccp_role(sk), sk, len);
rc = -EINVAL;
} else {
opt_recv->ccid3or_loss_event_rate = ntohl(*(__be32 *)value);
break;
case TFRC_OPT_RECEIVE_RATE:
if (unlikely(len != 4)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, invalid "
- "len for TFRC_OPT_RECEIVE_RATE\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, invalid len %d "
+ "for TFRC_OPT_RECEIVE_RATE\n",
+ dccp_role(sk), sk, len);
rc = -EINVAL;
} else {
opt_recv->ccid3or_receive_rate = ntohl(*(__be32 *)value);
* RX Half Connection methods
*/
-/* TFRC receiver states */
-enum ccid3_hc_rx_states {
- TFRC_RSTATE_NO_DATA = 1,
- TFRC_RSTATE_DATA,
- TFRC_RSTATE_TERM = 127,
-};
-
-#ifdef CCID3_DEBUG
+#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state)
{
static char *ccid3_rx_state_names[] = {
delta);
}
break;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hcrx->ccid3hcrx_state);
- dump_stack();
+ case TFRC_RSTATE_TERM:
+ DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
return;
}
packet = dccp_rx_hist_find_data_packet(&hcrx->ccid3hcrx_hist);
if (unlikely(packet == NULL)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, no data packet "
- "in history!\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, no data packet in history!\n",
+ dccp_role(sk), sk);
return;
}
hcrx->ccid3hcrx_tstamp_last_feedback = now;
- hcrx->ccid3hcrx_last_counter = packet->dccphrx_ccval;
- hcrx->ccid3hcrx_seqno_last_counter = packet->dccphrx_seqno;
+ hcrx->ccid3hcrx_ccval_last_counter = packet->dccphrx_ccval;
hcrx->ccid3hcrx_bytes_recv = 0;
/* Convert to multiples of 10us */
if (!(sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN))
return 0;
- DCCP_SKB_CB(skb)->dccpd_ccval = hcrx->ccid3hcrx_last_counter;
+ DCCP_SKB_CB(skb)->dccpd_ccval = hcrx->ccid3hcrx_ccval_last_counter;
if (dccp_packet_without_ack(skb))
return 0;
}
if (unlikely(step == 0)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, packet history "
- "contains no data packets!\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, packet history has no data packets!\n",
+ dccp_role(sk), sk);
return ~0;
}
if (unlikely(interval == 0)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, Could not find a "
- "win_count interval > 0. Defaulting to 1\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, Could not find a win_count interval > 0."
+ "Defaulting to 1\n", dccp_role(sk), sk);
interval = 1;
}
found:
+ if (!tail) {
+ DCCP_CRIT("tail is null\n");
+ return ~0;
+ }
rtt = timeval_delta(&tstamp, &tail->dccphrx_tstamp) * 4 / interval;
ccid3_pr_debug("%s, sk=%p, approximated RTT to %uus\n",
dccp_role(sk), sk, rtt);
- if (rtt == 0)
- rtt = 1;
+
+ if (rtt == 0) {
+ DCCP_WARN("RTT==0, setting to 1\n");
+ rtt = 1;
+ }
dccp_timestamp(sk, &tstamp);
delta = timeval_delta(&tstamp, &hcrx->ccid3hcrx_tstamp_last_feedback);
x_recv = usecs_div(hcrx->ccid3hcrx_bytes_recv, delta);
+ if (x_recv == 0)
+ x_recv = hcrx->ccid3hcrx_x_recv;
+
tmp1 = (u64)x_recv * (u64)rtt;
do_div(tmp1,10000000);
tmp2 = (u32)tmp1;
+
+ if (!tmp2) {
+ DCCP_CRIT("tmp2 = 0, x_recv = %u, rtt =%u\n", x_recv, rtt);
+ return ~0;
+ }
+
fval = (hcrx->ccid3hcrx_s * 100000) / tmp2;
/* do not alter order above or you will get overflow on 32 bit */
p = tfrc_calc_x_reverse_lookup(fval);
static void ccid3_hc_rx_update_li(struct sock *sk, u64 seq_loss, u8 win_loss)
{
struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
+ struct dccp_li_hist_entry *head;
+ u64 seq_temp;
- if (seq_loss != DCCP_MAX_SEQNO + 1 &&
- list_empty(&hcrx->ccid3hcrx_li_hist)) {
- struct dccp_li_hist_entry *li_tail;
+ if (list_empty(&hcrx->ccid3hcrx_li_hist)) {
+ if (!dccp_li_hist_interval_new(ccid3_li_hist,
+ &hcrx->ccid3hcrx_li_hist, seq_loss, win_loss))
+ return;
- li_tail = dccp_li_hist_interval_new(ccid3_li_hist,
- &hcrx->ccid3hcrx_li_hist,
- seq_loss, win_loss);
- if (li_tail == NULL)
+ head = list_entry(hcrx->ccid3hcrx_li_hist.next,
+ struct dccp_li_hist_entry, dccplih_node);
+ head->dccplih_interval = ccid3_hc_rx_calc_first_li(sk);
+ } else {
+ struct dccp_li_hist_entry *entry;
+ struct list_head *tail;
+
+ head = list_entry(hcrx->ccid3hcrx_li_hist.next,
+ struct dccp_li_hist_entry, dccplih_node);
+ /* FIXME win count check removed as was wrong */
+ /* should make this check with receive history */
+ /* and compare there as per section 10.2 of RFC4342 */
+
+ /* new loss event detected */
+ /* calculate last interval length */
+ seq_temp = dccp_delta_seqno(head->dccplih_seqno, seq_loss);
+ entry = dccp_li_hist_entry_new(ccid3_li_hist, SLAB_ATOMIC);
+
+ if (entry == NULL) {
+ DCCP_BUG("out of memory - can not allocate entry");
return;
- li_tail->dccplih_interval = ccid3_hc_rx_calc_first_li(sk);
- } else
- LIMIT_NETDEBUG(KERN_WARNING "%s: FIXME: find end of "
- "interval\n", __FUNCTION__);
+ }
+
+ list_add(&entry->dccplih_node, &hcrx->ccid3hcrx_li_hist);
+
+ tail = hcrx->ccid3hcrx_li_hist.prev;
+ list_del(tail);
+ kmem_cache_free(ccid3_li_hist->dccplih_slab, tail);
+
+ /* Create the newest interval */
+ entry->dccplih_seqno = seq_loss;
+ entry->dccplih_interval = seq_temp;
+ entry->dccplih_win_count = win_loss;
+ }
}
-static void ccid3_hc_rx_detect_loss(struct sock *sk)
+static int ccid3_hc_rx_detect_loss(struct sock *sk,
+ struct dccp_rx_hist_entry *packet)
{
struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
- u8 win_loss;
- const u64 seq_loss = dccp_rx_hist_detect_loss(&hcrx->ccid3hcrx_hist,
- &hcrx->ccid3hcrx_li_hist,
- &win_loss);
+ struct dccp_rx_hist_entry *rx_hist = dccp_rx_hist_head(&hcrx->ccid3hcrx_hist);
+ u64 seqno = packet->dccphrx_seqno;
+ u64 tmp_seqno;
+ int loss = 0;
+ u8 ccval;
+
- ccid3_hc_rx_update_li(sk, seq_loss, win_loss);
+ tmp_seqno = hcrx->ccid3hcrx_seqno_nonloss;
+
+ if (!rx_hist ||
+ follows48(packet->dccphrx_seqno, hcrx->ccid3hcrx_seqno_nonloss)) {
+ hcrx->ccid3hcrx_seqno_nonloss = seqno;
+ hcrx->ccid3hcrx_ccval_nonloss = packet->dccphrx_ccval;
+ goto detect_out;
+ }
+
+
+ while (dccp_delta_seqno(hcrx->ccid3hcrx_seqno_nonloss, seqno)
+ > TFRC_RECV_NUM_LATE_LOSS) {
+ loss = 1;
+ ccid3_hc_rx_update_li(sk, hcrx->ccid3hcrx_seqno_nonloss,
+ hcrx->ccid3hcrx_ccval_nonloss);
+ tmp_seqno = hcrx->ccid3hcrx_seqno_nonloss;
+ dccp_inc_seqno(&tmp_seqno);
+ hcrx->ccid3hcrx_seqno_nonloss = tmp_seqno;
+ dccp_inc_seqno(&tmp_seqno);
+ while (dccp_rx_hist_find_entry(&hcrx->ccid3hcrx_hist,
+ tmp_seqno, &ccval)) {
+ hcrx->ccid3hcrx_seqno_nonloss = tmp_seqno;
+ hcrx->ccid3hcrx_ccval_nonloss = ccval;
+ dccp_inc_seqno(&tmp_seqno);
+ }
+ }
+
+ /* FIXME - this code could be simplified with above while */
+ /* but works at moment */
+ if (follows48(packet->dccphrx_seqno, hcrx->ccid3hcrx_seqno_nonloss)) {
+ hcrx->ccid3hcrx_seqno_nonloss = seqno;
+ hcrx->ccid3hcrx_ccval_nonloss = packet->dccphrx_ccval;
+ }
+
+detect_out:
+ dccp_rx_hist_add_packet(ccid3_rx_hist, &hcrx->ccid3hcrx_hist,
+ &hcrx->ccid3hcrx_li_hist, packet,
+ hcrx->ccid3hcrx_seqno_nonloss);
+ return loss;
}
static void ccid3_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
struct dccp_rx_hist_entry *packet;
struct timeval now;
u8 win_count;
- u32 p_prev, r_sample, t_elapsed;
- int ins;
+ u32 p_prev, rtt_prev, r_sample, t_elapsed;
+ int loss;
- BUG_ON(hcrx == NULL ||
- !(hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA ||
- hcrx->ccid3hcrx_state == TFRC_RSTATE_DATA));
+ BUG_ON(hcrx == NULL);
opt_recv = &dccp_sk(sk)->dccps_options_received;
case DCCP_PKT_DATAACK:
if (opt_recv->dccpor_timestamp_echo == 0)
break;
- p_prev = hcrx->ccid3hcrx_rtt;
+ rtt_prev = hcrx->ccid3hcrx_rtt;
dccp_timestamp(sk, &now);
timeval_sub_usecs(&now, opt_recv->dccpor_timestamp_echo * 10);
r_sample = timeval_usecs(&now);
t_elapsed = opt_recv->dccpor_elapsed_time * 10;
if (unlikely(r_sample <= t_elapsed))
- LIMIT_NETDEBUG(KERN_WARNING "%s: r_sample=%uus, "
- "t_elapsed=%uus\n",
- __FUNCTION__, r_sample, t_elapsed);
+ DCCP_WARN("r_sample=%uus, t_elapsed=%uus\n",
+ r_sample, t_elapsed);
else
r_sample -= t_elapsed;
hcrx->ccid3hcrx_rtt = (hcrx->ccid3hcrx_rtt * 9) / 10 +
r_sample / 10;
- if (p_prev != hcrx->ccid3hcrx_rtt)
- ccid3_pr_debug("%s, New RTT=%luus, elapsed time=%u\n",
+ if (rtt_prev != hcrx->ccid3hcrx_rtt)
+ ccid3_pr_debug("%s, New RTT=%uus, elapsed time=%u\n",
dccp_role(sk), hcrx->ccid3hcrx_rtt,
opt_recv->dccpor_elapsed_time);
break;
packet = dccp_rx_hist_entry_new(ccid3_rx_hist, sk, opt_recv->dccpor_ndp,
skb, SLAB_ATOMIC);
if (unlikely(packet == NULL)) {
- LIMIT_NETDEBUG(KERN_WARNING "%s: %s, sk=%p, Not enough mem to "
- "add rx packet to history, consider it lost!\n",
- __FUNCTION__, dccp_role(sk), sk);
+ DCCP_WARN("%s, sk=%p, Not enough mem to add rx packet "
+ "to history, consider it lost!\n", dccp_role(sk), sk);
return;
}
win_count = packet->dccphrx_ccval;
- ins = dccp_rx_hist_add_packet(ccid3_rx_hist, &hcrx->ccid3hcrx_hist,
- &hcrx->ccid3hcrx_li_hist, packet);
+ loss = ccid3_hc_rx_detect_loss(sk, packet);
if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_ACK)
return;
case TFRC_RSTATE_DATA:
hcrx->ccid3hcrx_bytes_recv += skb->len -
dccp_hdr(skb)->dccph_doff * 4;
- if (ins != 0)
+ if (loss)
break;
dccp_timestamp(sk, &now);
ccid3_hc_rx_send_feedback(sk);
}
return;
- default:
- printk(KERN_CRIT "%s: %s, sk=%p, Illegal state (%d)!\n",
- __FUNCTION__, dccp_role(sk), sk, hcrx->ccid3hcrx_state);
- dump_stack();
+ case TFRC_RSTATE_TERM:
+ DCCP_BUG("Illegal %s state TERM, sk=%p", dccp_role(sk), sk);
return;
}
ccid3_pr_debug("%s, sk=%p(%s), data loss! Reacting...\n",
dccp_role(sk), sk, dccp_state_name(sk->sk_state));
- ccid3_hc_rx_detect_loss(sk);
p_prev = hcrx->ccid3hcrx_p;
/* Calculate loss event rate */
/* Scaling up by 1000000 as fixed decimal */
if (i_mean != 0)
hcrx->ccid3hcrx_p = 1000000 / i_mean;
- }
+ } else
+ DCCP_BUG("empty loss history");
if (hcrx->ccid3hcrx_p > p_prev) {
ccid3_hc_rx_send_feedback(sk);
}
static struct ccid_operations ccid3 = {
- .ccid_id = 3,
+ .ccid_id = DCCPC_CCID3,
.ccid_name = "ccid3",
.ccid_owner = THIS_MODULE,
.ccid_hc_tx_obj_size = sizeof(struct ccid3_hc_tx_sock),
.ccid_hc_tx_getsockopt = ccid3_hc_tx_getsockopt,
};
+#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
module_param(ccid3_debug, int, 0444);
MODULE_PARM_DESC(ccid3_debug, "Enable debug messages");
+#endif
static __init int ccid3_module_init(void)
{
}
module_exit(ccid3_module_exit);
-MODULE_AUTHOR("Ian McDonald <iam4@cs.waikato.ac.nz>, "
+MODULE_AUTHOR("Ian McDonald <ian.mcdonald@jandi.co.nz>, "
"Arnaldo Carvalho de Melo <acme@ghostprotocols.net>");
MODULE_DESCRIPTION("DCCP TFRC CCID3 CCID");
MODULE_LICENSE("GPL");