Contiki-Inga 3.x
csma.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, Swedish Institute of Computer Science.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * A Carrier Sense Multiple Access (CSMA) MAC layer
36  * \author
37  * Adam Dunkels <adam@sics.se>
38  */
39 
40 #include "net/mac/csma.h"
41 #include "net/packetbuf.h"
42 #include "net/queuebuf.h"
43 
44 #include "sys/ctimer.h"
45 #include "sys/clock.h"
46 
47 #include "lib/random.h"
48 
49 #include "net/netstack.h"
50 
51 #include "lib/list.h"
52 #include "lib/memb.h"
53 
54 #include <string.h>
55 
56 #include <stdio.h>
57 
58 #define DEBUG 0
59 #if DEBUG
60 #include <stdio.h>
61 #define PRINTF(...) printf(__VA_ARGS__)
62 #else /* DEBUG */
63 #define PRINTF(...)
64 #endif /* DEBUG */
65 
66 #ifndef CSMA_MAX_BACKOFF_EXPONENT
67 #ifdef CSMA_CONF_MAX_BACKOFF_EXPONENT
68 #define CSMA_MAX_BACKOFF_EXPONENT CSMA_CONF_MAX_BACKOFF_EXPONENT
69 #else
70 #define CSMA_MAX_BACKOFF_EXPONENT 3
71 #endif /* CSMA_CONF_MAX_BACKOFF_EXPONENT */
72 #endif /* CSMA_MAX_BACKOFF_EXPONENT */
73 
74 #ifndef CSMA_MAX_MAC_TRANSMISSIONS
75 #ifdef CSMA_CONF_MAX_MAC_TRANSMISSIONS
76 #define CSMA_MAX_MAC_TRANSMISSIONS CSMA_CONF_MAX_MAC_TRANSMISSIONS
77 #else
78 #define CSMA_MAX_MAC_TRANSMISSIONS 3
79 #endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS */
80 #endif /* CSMA_MAX_MAC_TRANSMISSIONS */
81 
82 #if CSMA_MAX_MAC_TRANSMISSIONS < 1
83 #error CSMA_CONF_MAX_MAC_TRANSMISSIONS must be at least 1.
84 #error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile.
85 #endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */
86 
87 /* Packet metadata */
88 struct qbuf_metadata {
89  mac_callback_t sent;
90  void *cptr;
91  uint8_t max_transmissions;
92 };
93 
94 /* Every neighbor has its own packet queue */
95 struct neighbor_queue {
96  struct neighbor_queue *next;
97  linkaddr_t addr;
98  struct ctimer transmit_timer;
99  uint8_t transmissions;
100  uint8_t collisions, deferrals;
101  LIST_STRUCT(queued_packet_list);
102 };
103 
104 /* The maximum number of co-existing neighbor queues */
105 #ifdef CSMA_CONF_MAX_NEIGHBOR_QUEUES
106 #define CSMA_MAX_NEIGHBOR_QUEUES CSMA_CONF_MAX_NEIGHBOR_QUEUES
107 #else
108 #define CSMA_MAX_NEIGHBOR_QUEUES 2
109 #endif /* CSMA_CONF_MAX_NEIGHBOR_QUEUES */
110 
111 #define MAX_QUEUED_PACKETS QUEUEBUF_NUM
112 MEMB(neighbor_memb, struct neighbor_queue, CSMA_MAX_NEIGHBOR_QUEUES);
113 MEMB(packet_memb, struct rdc_buf_list, MAX_QUEUED_PACKETS);
114 MEMB(metadata_memb, struct qbuf_metadata, MAX_QUEUED_PACKETS);
115 LIST(neighbor_list);
116 
117 static void packet_sent(void *ptr, int status, int num_transmissions);
118 static void transmit_packet_list(void *ptr);
119 
120 /*---------------------------------------------------------------------------*/
121 static struct neighbor_queue *
122 neighbor_queue_from_addr(const linkaddr_t *addr)
123 {
124  struct neighbor_queue *n = list_head(neighbor_list);
125  while(n != NULL) {
126  if(linkaddr_cmp(&n->addr, addr)) {
127  return n;
128  }
129  n = list_item_next(n);
130  }
131  return NULL;
132 }
133 /*---------------------------------------------------------------------------*/
134 static clock_time_t
135 default_timebase(void)
136 {
137  clock_time_t time;
138  /* The retransmission time must be proportional to the channel
139  check interval of the underlying radio duty cycling layer. */
140  time = NETSTACK_RDC.channel_check_interval();
141 
142  /* If the radio duty cycle has no channel check interval (i.e., it
143  does not turn the radio off), we make the retransmission time
144  proportional to the configured MAC channel check rate. */
145  if(time == 0) {
146  time = CLOCK_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE;
147  }
148  return time;
149 }
150 /*---------------------------------------------------------------------------*/
151 static void
152 transmit_packet_list(void *ptr)
153 {
154  struct neighbor_queue *n = ptr;
155  if(n) {
156  struct rdc_buf_list *q = list_head(n->queued_packet_list);
157  if(q != NULL) {
158  PRINTF("csma: preparing number %d %p, queue len %d\n", n->transmissions, q,
159  list_length(n->queued_packet_list));
160  /* Send packets in the neighbor's list */
161  NETSTACK_RDC.send_list(packet_sent, n, q);
162  }
163  }
164 }
165 /*---------------------------------------------------------------------------*/
166 static void
167 free_packet(struct neighbor_queue *n, struct rdc_buf_list *p)
168 {
169  if(p != NULL) {
170  /* Remove packet from list and deallocate */
171  list_remove(n->queued_packet_list, p);
172 
173  queuebuf_free(p->buf);
174  memb_free(&metadata_memb, p->ptr);
175  memb_free(&packet_memb, p);
176  PRINTF("csma: free_queued_packet, queue length %d\n",
177  list_length(n->queued_packet_list));
178  if(list_head(n->queued_packet_list) != NULL) {
179  /* There is a next packet. We reset current tx information */
180  n->transmissions = 0;
181  n->collisions = 0;
182  n->deferrals = 0;
183  /* Set a timer for next transmissions */
184  ctimer_set(&n->transmit_timer, default_timebase(),
185  transmit_packet_list, n);
186  } else {
187  /* This was the last packet in the queue, we free the neighbor */
188  ctimer_stop(&n->transmit_timer);
189  list_remove(neighbor_list, n);
190  memb_free(&neighbor_memb, n);
191  }
192  }
193 }
194 /*---------------------------------------------------------------------------*/
195 static void
196 packet_sent(void *ptr, int status, int num_transmissions)
197 {
198  struct neighbor_queue *n;
199  struct rdc_buf_list *q;
200  struct qbuf_metadata *metadata;
201  clock_time_t time = 0;
202  mac_callback_t sent;
203  void *cptr;
204  int num_tx;
205  int backoff_exponent;
206  int backoff_transmissions;
207 
208  n = ptr;
209  if(n == NULL) {
210  return;
211  }
212  switch(status) {
213  case MAC_TX_OK:
214  case MAC_TX_NOACK:
215  n->transmissions += num_transmissions;
216  break;
217  case MAC_TX_COLLISION:
218  n->collisions += num_transmissions;
219  break;
220  case MAC_TX_DEFERRED:
221  n->deferrals += num_transmissions;
222  break;
223  }
224 
225  /* Find out what packet this callback refers to */
226  for(q = list_head(n->queued_packet_list);
227  q != NULL; q = list_item_next(q)) {
228  if(queuebuf_attr(q->buf, PACKETBUF_ATTR_MAC_SEQNO) ==
229  packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO)) {
230  break;
231  }
232  }
233 
234  if(q != NULL) {
235  metadata = (struct qbuf_metadata *)q->ptr;
236 
237  if(metadata != NULL) {
238  sent = metadata->sent;
239  cptr = metadata->cptr;
240  num_tx = n->transmissions;
241  if(status == MAC_TX_COLLISION ||
242  status == MAC_TX_NOACK) {
243 
244  /* If the transmission was not performed because of a
245  collision or noack, we must retransmit the packet. */
246 
247  switch(status) {
248  case MAC_TX_COLLISION:
249  PRINTF("csma: rexmit collision %d\n", n->transmissions);
250  break;
251  case MAC_TX_NOACK:
252  PRINTF("csma: rexmit noack %d\n", n->transmissions);
253  break;
254  default:
255  PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions);
256  }
257 
258  /* The retransmission time must be proportional to the channel
259  check interval of the underlying radio duty cycling layer. */
260  time = default_timebase();
261 
262  /* The retransmission time uses a truncated exponential backoff
263  * so that the interval between the transmissions increase with
264  * each retransmit. */
265  backoff_exponent = num_tx;
266 
267  /* Truncate the exponent if needed. */
268  if(backoff_exponent > CSMA_MAX_BACKOFF_EXPONENT) {
269  backoff_exponent = CSMA_MAX_BACKOFF_EXPONENT;
270  }
271 
272  /* Proceed to exponentiation. */
273  backoff_transmissions = 1 << backoff_exponent;
274 
275  /* Pick a time for next transmission, within the interval:
276  * [time, time + 2^backoff_exponent * time[ */
277  time = time + (random_rand() % (backoff_transmissions * time));
278 
279  if(n->transmissions < metadata->max_transmissions) {
280  PRINTF("csma: retransmitting with time %lu %p\n", time, q);
281  ctimer_set(&n->transmit_timer, time,
282  transmit_packet_list, n);
283  /* This is needed to correctly attribute energy that we spent
284  transmitting this packet. */
285  queuebuf_update_attr_from_packetbuf(q->buf);
286  } else {
287  PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n",
288  status, n->transmissions, n->collisions);
289  free_packet(n, q);
290  mac_call_sent_callback(sent, cptr, status, num_tx);
291  }
292  } else {
293  if(status == MAC_TX_OK) {
294  PRINTF("csma: rexmit ok %d\n", n->transmissions);
295  } else {
296  PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status);
297  }
298  free_packet(n, q);
299  mac_call_sent_callback(sent, cptr, status, num_tx);
300  }
301  }
302  }
303 }
304 /*---------------------------------------------------------------------------*/
305 static void
306 send_packet(mac_callback_t sent, void *ptr)
307 {
308  struct rdc_buf_list *q;
309  struct neighbor_queue *n;
310  static uint8_t initialized = 0;
311  static uint16_t seqno;
312  const linkaddr_t *addr = packetbuf_addr(PACKETBUF_ADDR_RECEIVER);
313 
314  if(!initialized) {
315  initialized = 1;
316  /* Initialize the sequence number to a random value as per 802.15.4. */
317  seqno = random_rand();
318  }
319 
320  if(seqno == 0) {
321  /* PACKETBUF_ATTR_MAC_SEQNO cannot be zero, due to a pecuilarity
322  in framer-802154.c. */
323  seqno++;
324  }
325  packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, seqno++);
326 
327  /* Look for the neighbor entry */
328  n = neighbor_queue_from_addr(addr);
329  if(n == NULL) {
330  /* Allocate a new neighbor entry */
331  n = memb_alloc(&neighbor_memb);
332  if(n != NULL) {
333  /* Init neighbor entry */
334  linkaddr_copy(&n->addr, addr);
335  n->transmissions = 0;
336  n->collisions = 0;
337  n->deferrals = 0;
338  /* Init packet list for this neighbor */
339  LIST_STRUCT_INIT(n, queued_packet_list);
340  /* Add neighbor to the list */
341  list_add(neighbor_list, n);
342  }
343  }
344 
345  if(n != NULL) {
346  /* Add packet to the neighbor's queue */
347  q = memb_alloc(&packet_memb);
348  if(q != NULL) {
349  q->ptr = memb_alloc(&metadata_memb);
350  if(q->ptr != NULL) {
351  q->buf = queuebuf_new_from_packetbuf();
352  if(q->buf != NULL) {
353  struct qbuf_metadata *metadata = (struct qbuf_metadata *)q->ptr;
354  /* Neighbor and packet successfully allocated */
355  if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) {
356  /* Use default configuration for max transmissions */
357  metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS;
358  } else {
359  metadata->max_transmissions =
360  packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS);
361  }
362  metadata->sent = sent;
363  metadata->cptr = ptr;
364 
365  if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) ==
366  PACKETBUF_ATTR_PACKET_TYPE_ACK) {
367  list_push(n->queued_packet_list, q);
368  } else {
369  list_add(n->queued_packet_list, q);
370  }
371 
372  /* If q is the first packet in the neighbor's queue, send asap */
373  if(list_head(n->queued_packet_list) == q) {
374  ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n);
375  }
376  return;
377  }
378  memb_free(&metadata_memb, q->ptr);
379  PRINTF("csma: could not allocate queuebuf, dropping packet\n");
380  }
381  memb_free(&packet_memb, q);
382  PRINTF("csma: could not allocate queuebuf, dropping packet\n");
383  }
384  /* The packet allocation failed. Remove and free neighbor entry if empty. */
385  if(list_length(n->queued_packet_list) == 0) {
386  list_remove(neighbor_list, n);
387  memb_free(&neighbor_memb, n);
388  }
389  PRINTF("csma: could not allocate packet, dropping packet\n");
390  } else {
391  PRINTF("csma: could not allocate neighbor, dropping packet\n");
392  }
393  mac_call_sent_callback(sent, ptr, MAC_TX_ERR, 1);
394 }
395 /*---------------------------------------------------------------------------*/
396 static void
397 input_packet(void)
398 {
399  NETSTACK_NETWORK.input();
400 }
401 /*---------------------------------------------------------------------------*/
402 static int
403 on(void)
404 {
405  return NETSTACK_RDC.on();
406 }
407 /*---------------------------------------------------------------------------*/
408 static int
409 off(int keep_radio_on)
410 {
411  return NETSTACK_RDC.off(keep_radio_on);
412 }
413 /*---------------------------------------------------------------------------*/
414 static unsigned short
415 channel_check_interval(void)
416 {
417  if(NETSTACK_RDC.channel_check_interval) {
418  return NETSTACK_RDC.channel_check_interval();
419  }
420  return 0;
421 }
422 /*---------------------------------------------------------------------------*/
423 static void
424 init(void)
425 {
426  memb_init(&packet_memb);
427  memb_init(&metadata_memb);
428  memb_init(&neighbor_memb);
429 }
430 /*---------------------------------------------------------------------------*/
431 const struct mac_driver csma_driver = {
432  "CSMA",
433  init,
434  send_packet,
435  input_packet,
436  on,
437  off,
439 };
440 /*---------------------------------------------------------------------------*/