Merge branch 'master' of ssh://contiki.git.sourceforge.net/gitroot/contiki/contiki
This commit is contained in:
commit
bef9b2bd36
@ -228,6 +228,12 @@ ifndef CUSTOM_RULE_LINK
|
||||
$(LD) $(LDFLAGS) $(TARGET_STARTFILES) ${filter-out %.a,$^} ${filter %.a,$^} $(TARGET_LIBFILES) -o $@
|
||||
endif
|
||||
|
||||
%.ramprof: %.$(TARGET)
|
||||
nm -S -td --size-sort $< | grep -i " [abdrw] " | cut -d' ' -f2,4
|
||||
|
||||
%.flashprof: %.$(TARGET)
|
||||
nm -S -td --size-sort $< | grep -i " [t] " | cut -d' ' -f2,4
|
||||
|
||||
# Don't treat %.$(TARGET) as an intermediate file because it is
|
||||
# in fact the primary target.
|
||||
.PRECIOUS: %.$(TARGET)
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "dev/watchdog.h"
|
||||
|
||||
#include "net/rime.h"
|
||||
#include "net/netstack.h"
|
||||
#include "dev/cc2420.h"
|
||||
#include "dev/leds.h"
|
||||
#include "dev/sht11.h"
|
||||
@ -97,7 +98,7 @@ do_rssi(void)
|
||||
static int sample;
|
||||
int channel;
|
||||
|
||||
rime_mac->off(0);
|
||||
NETSTACK_MAC.off(0);
|
||||
|
||||
cc2420_on();
|
||||
for(channel = 11; channel <= 26; ++channel) {
|
||||
@ -105,7 +106,7 @@ do_rssi(void)
|
||||
rssi_samples[sample].channel[channel - 11] = cc2420_rssi() + 53;
|
||||
}
|
||||
|
||||
rime_mac->on();
|
||||
NETSTACK_MAC.on();
|
||||
|
||||
sample = (sample + 1) % NUM_SAMPLES;
|
||||
|
||||
|
@ -522,7 +522,7 @@ static int
|
||||
send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_list *buf_list)
|
||||
{
|
||||
rtimer_clock_t t0;
|
||||
rtimer_clock_t encounter_time = 0, previous_txtime = 0;
|
||||
rtimer_clock_t encounter_time = 0;
|
||||
int strobes;
|
||||
uint8_t got_strobe_ack = 0;
|
||||
int hdrlen, len;
|
||||
@ -723,7 +723,6 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_
|
||||
watchdog_periodic();
|
||||
t0 = RTIMER_NOW();
|
||||
seqno = packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO);
|
||||
previous_txtime = RTIMER_NOW();
|
||||
for(strobes = 0, collisions = 0;
|
||||
got_strobe_ack == 0 && collisions == 0 &&
|
||||
RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + STROBE_TIME); strobes++) {
|
||||
@ -751,7 +750,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_
|
||||
if(ret == RADIO_TX_OK) {
|
||||
if(!is_broadcast) {
|
||||
got_strobe_ack = 1;
|
||||
encounter_time = previous_txtime;
|
||||
encounter_time = txtime;
|
||||
break;
|
||||
}
|
||||
} else if (ret == RADIO_TX_NOACK) {
|
||||
@ -776,7 +775,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_
|
||||
len = NETSTACK_RADIO.read(ackbuf, ACK_LEN);
|
||||
if(len == ACK_LEN && seqno == ackbuf[ACK_LEN-1]) {
|
||||
got_strobe_ack = 1;
|
||||
encounter_time = previous_txtime;
|
||||
encounter_time = txtime;
|
||||
break;
|
||||
} else {
|
||||
PRINTF("contikimac: collisions while sending\n");
|
||||
@ -784,8 +783,6 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_
|
||||
}
|
||||
}
|
||||
#endif /* RDC_CONF_HARDWARE_ACK */
|
||||
|
||||
previous_txtime = txtime;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +89,6 @@ void rime_input(void);
|
||||
|
||||
int rime_output(struct channel *c);
|
||||
|
||||
extern const struct mac_driver *rime_mac;
|
||||
|
||||
struct rime_sniffer {
|
||||
struct rime_sniffer *next;
|
||||
void (* input_callback)(void);
|
||||
|
@ -61,8 +61,6 @@
|
||||
|
||||
#include "lib/list.h"
|
||||
|
||||
const struct mac_driver *rime_mac;
|
||||
|
||||
#ifdef RIME_CONF_BROADCAST_ANNOUNCEMENT_CHANNEL
|
||||
#define BROADCAST_ANNOUNCEMENT_CHANNEL RIME_CONF_BROADCAST_ANNOUNCEMENT_CHANNEL
|
||||
#else /* RIME_CONF_BROADCAST_ANNOUNCEMENT_CHANNEL */
|
||||
@ -130,7 +128,6 @@ init(void)
|
||||
packetbuf_clear();
|
||||
announcement_init();
|
||||
|
||||
rime_mac = &NETSTACK_MAC;
|
||||
chameleon_init();
|
||||
|
||||
/* XXX This is initializes the transmission of announcements but it
|
||||
|
@ -211,6 +211,11 @@ static uint8_t rime_payload_len;
|
||||
* is used this includes the UDP header in addition to the IP header).
|
||||
*/
|
||||
static uint8_t uncomp_hdr_len;
|
||||
|
||||
/**
|
||||
* the result of the last transmitted fragment
|
||||
*/
|
||||
static int last_tx_status;
|
||||
/** @} */
|
||||
|
||||
#if SICSLOWPAN_CONF_FRAG
|
||||
@ -234,7 +239,7 @@ static uip_buf_t sicslowpan_aligned_buf;
|
||||
* length of the ip packet already sent / received.
|
||||
* It includes IP and transport headers.
|
||||
*/
|
||||
static uint16_t processed_ip_len;
|
||||
static uint16_t processed_ip_in_len;
|
||||
|
||||
/** Datagram tag to be put in the fragments I send. */
|
||||
static uint16_t my_tag;
|
||||
@ -1308,6 +1313,7 @@ packet_sent(void *ptr, int status, int transmissions)
|
||||
if(callback != NULL) {
|
||||
callback->output_callback(status);
|
||||
}
|
||||
last_tx_status = status;
|
||||
}
|
||||
/*--------------------------------------------------------------------*/
|
||||
/**
|
||||
@ -1353,6 +1359,9 @@ output(uip_lladdr_t *localdest)
|
||||
/* The MAC address of the destination of the packet */
|
||||
rimeaddr_t dest;
|
||||
|
||||
/* Number of bytes processed. */
|
||||
uint16_t processed_ip_out_len;
|
||||
|
||||
/* init */
|
||||
uncomp_hdr_len = 0;
|
||||
rime_hdr_len = 0;
|
||||
@ -1443,6 +1452,7 @@ output(uip_lladdr_t *localdest)
|
||||
((SICSLOWPAN_DISPATCH_FRAG1 << 8) | uip_len));
|
||||
/* RIME_FRAG_BUF->tag = uip_htons(my_tag); */
|
||||
SET16(RIME_FRAG_PTR, RIME_FRAG_TAG, my_tag);
|
||||
my_tag++;
|
||||
|
||||
/* Copy payload and send */
|
||||
rime_hdr_len += SICSLOWPAN_FRAG1_HDR_LEN;
|
||||
@ -1461,8 +1471,16 @@ output(uip_lladdr_t *localdest)
|
||||
queuebuf_free(q);
|
||||
q = NULL;
|
||||
|
||||
/* set processed_ip_len to what we already sent from the IP payload*/
|
||||
processed_ip_len = rime_payload_len + uncomp_hdr_len;
|
||||
/* Check tx result. */
|
||||
if((last_tx_status == MAC_TX_COLLISION) ||
|
||||
(last_tx_status == MAC_TX_ERR) ||
|
||||
(last_tx_status == MAC_TX_ERR_FATAL)) {
|
||||
PRINTFO("error in fragment tx, dropping subsequent fragments.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set processed_ip_out_len to what we already sent from the IP payload*/
|
||||
processed_ip_out_len = rime_payload_len + uncomp_hdr_len;
|
||||
|
||||
/*
|
||||
* Create following fragments
|
||||
@ -1475,19 +1493,19 @@ output(uip_lladdr_t *localdest)
|
||||
SET16(RIME_FRAG_PTR, RIME_FRAG_DISPATCH_SIZE,
|
||||
((SICSLOWPAN_DISPATCH_FRAGN << 8) | uip_len));
|
||||
rime_payload_len = (MAC_MAX_PAYLOAD - rime_hdr_len) & 0xf8;
|
||||
while(processed_ip_len < uip_len) {
|
||||
while(processed_ip_out_len < uip_len) {
|
||||
PRINTFO("sicslowpan output: fragment ");
|
||||
RIME_FRAG_PTR[RIME_FRAG_OFFSET] = processed_ip_len >> 3;
|
||||
RIME_FRAG_PTR[RIME_FRAG_OFFSET] = processed_ip_out_len >> 3;
|
||||
|
||||
/* Copy payload and send */
|
||||
if(uip_len - processed_ip_len < rime_payload_len) {
|
||||
if(uip_len - processed_ip_out_len < rime_payload_len) {
|
||||
/* last fragment */
|
||||
rime_payload_len = uip_len - processed_ip_len;
|
||||
rime_payload_len = uip_len - processed_ip_out_len;
|
||||
}
|
||||
PRINTFO("(offset %d, len %d, tag %d)\n",
|
||||
processed_ip_len >> 3, rime_payload_len, my_tag);
|
||||
processed_ip_out_len >> 3, rime_payload_len, my_tag);
|
||||
memcpy(rime_ptr + rime_hdr_len,
|
||||
(uint8_t *)UIP_IP_BUF + processed_ip_len, rime_payload_len);
|
||||
(uint8_t *)UIP_IP_BUF + processed_ip_out_len, rime_payload_len);
|
||||
packetbuf_set_datalen(rime_payload_len + rime_hdr_len);
|
||||
q = queuebuf_new_from_packetbuf();
|
||||
if(q == NULL) {
|
||||
@ -1498,12 +1516,16 @@ output(uip_lladdr_t *localdest)
|
||||
queuebuf_to_packetbuf(q);
|
||||
queuebuf_free(q);
|
||||
q = NULL;
|
||||
processed_ip_len += rime_payload_len;
|
||||
processed_ip_out_len += rime_payload_len;
|
||||
|
||||
/* Check tx result. */
|
||||
if((last_tx_status == MAC_TX_COLLISION) ||
|
||||
(last_tx_status == MAC_TX_ERR) ||
|
||||
(last_tx_status == MAC_TX_ERR_FATAL)) {
|
||||
PRINTFO("error in fragment tx, dropping subsequent fragments.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* end: reset global variables */
|
||||
my_tag++;
|
||||
processed_ip_len = 0;
|
||||
#else /* SICSLOWPAN_CONF_FRAG */
|
||||
PRINTFO("sicslowpan output: Packet too large to be sent without fragmentation support; dropping packet\n");
|
||||
return 0;
|
||||
@ -1558,7 +1580,7 @@ input(void)
|
||||
/* if reassembly timed out, cancel it */
|
||||
if(timer_expired(&reass_timer)) {
|
||||
sicslowpan_len = 0;
|
||||
processed_ip_len = 0;
|
||||
processed_ip_in_len = 0;
|
||||
}
|
||||
/*
|
||||
* Since we don't support the mesh and broadcast header, the first header
|
||||
@ -1593,10 +1615,10 @@ input(void)
|
||||
|
||||
/* If this is the last fragment, we may shave off any extrenous
|
||||
bytes at the end. We must be liberal in what we accept. */
|
||||
PRINTFI("last_fragment?: processed_ip_len %d rime_payload_len %d frag_size %d\n",
|
||||
processed_ip_len, packetbuf_datalen() - rime_hdr_len, frag_size);
|
||||
PRINTFI("last_fragment?: processed_ip_in_len %d rime_payload_len %d frag_size %d\n",
|
||||
processed_ip_in_len, packetbuf_datalen() - rime_hdr_len, frag_size);
|
||||
|
||||
if(processed_ip_len + packetbuf_datalen() - rime_hdr_len >= frag_size) {
|
||||
if(processed_ip_in_len + packetbuf_datalen() - rime_hdr_len >= frag_size) {
|
||||
last_fragment = 1;
|
||||
}
|
||||
break;
|
||||
@ -1604,7 +1626,7 @@ input(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if(processed_ip_len > 0) {
|
||||
if(processed_ip_in_len > 0) {
|
||||
/* reassembly is ongoing */
|
||||
/* printf("frag %d %d\n", reass_tag, frag_tag);*/
|
||||
if((frag_size > 0 &&
|
||||
@ -1624,7 +1646,7 @@ input(void)
|
||||
* reassembly is off
|
||||
* start it if we received a fragment
|
||||
*/
|
||||
if(frag_size > 0) {
|
||||
if((frag_size > 0) && (frag_size <= UIP_BUFSIZE)) {
|
||||
sicslowpan_len = frag_size;
|
||||
reass_tag = frag_tag;
|
||||
timer_set(&reass_timer, SICSLOWPAN_REASS_MAXAGE*CLOCK_SECOND);
|
||||
@ -1690,22 +1712,22 @@ input(void)
|
||||
rime_payload_len = packetbuf_datalen() - rime_hdr_len;
|
||||
memcpy((uint8_t *)SICSLOWPAN_IP_BUF + uncomp_hdr_len + (uint16_t)(frag_offset << 3), rime_ptr + rime_hdr_len, rime_payload_len);
|
||||
|
||||
/* update processed_ip_len if fragment, sicslowpan_len otherwise */
|
||||
/* update processed_ip_in_len if fragment, sicslowpan_len otherwise */
|
||||
|
||||
#if SICSLOWPAN_CONF_FRAG
|
||||
if(frag_size > 0) {
|
||||
/* Add the size of the header only for the first fragment. */
|
||||
if(first_fragment != 0) {
|
||||
processed_ip_len += uncomp_hdr_len;
|
||||
processed_ip_in_len += uncomp_hdr_len;
|
||||
}
|
||||
/* For the last fragment, we are OK if there is extrenous bytes at
|
||||
the end of the packet. */
|
||||
if(last_fragment != 0) {
|
||||
processed_ip_len = frag_size;
|
||||
processed_ip_in_len = frag_size;
|
||||
} else {
|
||||
processed_ip_len += rime_payload_len;
|
||||
processed_ip_in_len += rime_payload_len;
|
||||
}
|
||||
PRINTF("processed_ip_len %d, rime_payload_len %d\n", processed_ip_len, rime_payload_len);
|
||||
PRINTF("processed_ip_in_len %d, rime_payload_len %d\n", processed_ip_in_len, rime_payload_len);
|
||||
|
||||
} else {
|
||||
#endif /* SICSLOWPAN_CONF_FRAG */
|
||||
@ -1717,15 +1739,15 @@ input(void)
|
||||
* If we have a full IP packet in sicslowpan_buf, deliver it to
|
||||
* the IP stack
|
||||
*/
|
||||
PRINTF("sicslowpan_init processed_ip_len %d, sicslowpan_len %d\n",
|
||||
processed_ip_len, sicslowpan_len);
|
||||
if(processed_ip_len == 0 || (processed_ip_len == sicslowpan_len)) {
|
||||
PRINTF("sicslowpan_init processed_ip_in_len %d, sicslowpan_len %d\n",
|
||||
processed_ip_in_len, sicslowpan_len);
|
||||
if(processed_ip_in_len == 0 || (processed_ip_in_len == sicslowpan_len)) {
|
||||
PRINTFI("sicslowpan input: IP packet ready (length %d)\n",
|
||||
sicslowpan_len);
|
||||
memcpy((uint8_t *)UIP_IP_BUF, (uint8_t *)SICSLOWPAN_IP_BUF, sicslowpan_len);
|
||||
uip_len = sicslowpan_len;
|
||||
sicslowpan_len = 0;
|
||||
processed_ip_len = 0;
|
||||
processed_ip_in_len = 0;
|
||||
#endif /* SICSLOWPAN_CONF_FRAG */
|
||||
|
||||
#if DEBUG
|
||||
|
@ -570,6 +570,7 @@ tcpip_ipv6_output(void)
|
||||
PRINTF("FALLBACK: removing ext hdrs & setting proto %d %d\n",
|
||||
uip_ext_len, *((uint8_t *)UIP_IP_BUF + 40));
|
||||
if(uip_ext_len > 0) {
|
||||
extern void remove_ext_hdr(void);
|
||||
uint8_t proto = *((uint8_t *)UIP_IP_BUF + 40);
|
||||
remove_ext_hdr();
|
||||
/* This should be copied from the ext header... */
|
||||
@ -643,7 +644,6 @@ tcpip_ipv6_output(void)
|
||||
PRINTF("tcpip_ipv6_output: nbr cache entry stale moving to delay\n");
|
||||
}
|
||||
|
||||
stimer_set(&nbr->sendns, uip_ds6_if.retrans_timer / 1000);
|
||||
tcpip_output(&nbr->lladdr);
|
||||
|
||||
#if UIP_CONF_IPV6_QUEUE_PKT
|
||||
|
@ -212,12 +212,11 @@ uip_ds6_periodic(void)
|
||||
}
|
||||
break;
|
||||
case NBR_DELAY:
|
||||
if(stimer_expired(&locnbr->reachable) && (uip_len == 0)) {
|
||||
if(stimer_expired(&locnbr->reachable)) {
|
||||
locnbr->state = NBR_PROBE;
|
||||
locnbr->nscount = 1;
|
||||
PRINTF("DELAY: moving to PROBE + NS %u\n", locnbr->nscount);
|
||||
uip_nd6_ns_output(NULL, &locnbr->ipaddr, &locnbr->ipaddr);
|
||||
stimer_set(&locnbr->sendns, uip_ds6_if.retrans_timer / 1000);
|
||||
locnbr->nscount = 0;
|
||||
PRINTF("DELAY: moving to PROBE\n");
|
||||
stimer_set(&locnbr->sendns, 0);
|
||||
}
|
||||
break;
|
||||
case NBR_PROBE:
|
||||
|
@ -166,8 +166,8 @@ loader-init.o: ${CONTIKI_TARGET}/loader/loader-init.S
|
||||
$(AS) -o $(notdir $(<:.S=.o)) $<
|
||||
# cp loader-init.o build-app/
|
||||
|
||||
.PHONY: symbols.c symbols.h
|
||||
ifdef CORE
|
||||
.PHONY: symbols.c symbols.h
|
||||
symbols.c:
|
||||
$(NM) $(CORE) | awk -f $(CONTIKI)/tools/mknmlist > symbols.c
|
||||
else
|
||||
|
@ -1,6 +1,11 @@
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
define \n
|
||||
|
||||
|
||||
endef
|
||||
|
||||
ifdef IAR
|
||||
${info Using IAR...}
|
||||
#IAR_PATH = C:/Program\ Files/IAR\ Systems/Embedded\ Workbench\ 5.4\ Evaluation
|
||||
@ -18,7 +23,7 @@ CONTIKI_CPU_DIRS = . dev hal simplemac hal/micro/cortexm3 hal/micro/cortexm3/stm
|
||||
|
||||
STM32W_C = leds-arch.c leds.c clock.c watchdog.c uart1.c uart1-putchar.c slip_uart1.c slip.c\
|
||||
stm32w-radio.c stm32w_systick.c uip_arch.c rtimer-arch.c adc.c micro.c sleep.c \
|
||||
micro-common.c micro-common-internal.c clocks.c mfg-token.c nvm.c flash.c rand.c system-timer.c
|
||||
micro-common.c micro-common-internal.c clocks.c mfg-token.c nvm.c flash.c rand.c system-timer.c mpu.c
|
||||
|
||||
STM32W_S = spmr.s79 context-switch.s79
|
||||
|
||||
@ -163,7 +168,7 @@ endif
|
||||
|
||||
|
||||
|
||||
FLASHER = $(CONTIKI)/tools/stm32w/stm32w_flasher/stm32w_flasher
|
||||
FLASHER = sudo $(CONTIKI)/tools/stm32w/stm32w_flasher/stm32w_flasher
|
||||
|
||||
# Check if we are running under Windows
|
||||
ifeq ($(HOST_OS),Windows)
|
||||
@ -224,11 +229,11 @@ CUSTOM_RULE_C_TO_CE = 1
|
||||
|
||||
CUSTOM_RULE_LINK = 1
|
||||
|
||||
.PHONY: symbols.c symbols.h
|
||||
ifdef CORE
|
||||
ifeq ($(wildcard $(CORE)),)
|
||||
${error $(CORE) doesn't exist}
|
||||
endif
|
||||
.PHONY: symbols.c symbols.h
|
||||
symbols.c:
|
||||
$(NM) $(CORE) | awk -f $(CONTIKI)/tools/mknmlist > symbols.c
|
||||
else
|
||||
@ -250,6 +255,19 @@ endif
|
||||
|
||||
endif #IAR
|
||||
|
||||
MOTELIST = $(CONTIKI)/tools/stm32w/motelist-linux
|
||||
|
||||
MOTES = $(shell $(MOTELIST) 2>&- | grep USB | \
|
||||
cut -f 4 -d \ | \
|
||||
perl -ne 'print $$1 . " " if(m-(/dev/\w+)-);')
|
||||
|
||||
motelist: stm-motelist
|
||||
|
||||
stm-motelist:
|
||||
$(MOTELIST)
|
||||
stm-motes:
|
||||
@echo $(MOTES)
|
||||
|
||||
$(OBJECTDIR)/%.o: %.s79
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
@ -258,7 +276,24 @@ $(OBJECTDIR)/%.o: %.s
|
||||
|
||||
%.bin: %.$(TARGET)
|
||||
$(OBJCOPY) $(OBJOPTS) $< $@
|
||||
|
||||
%.flash: %.bin
|
||||
$(FLASHER) $(FLASHEROPTS) $<
|
||||
|
||||
|
||||
# reset all stm32w devices sequentially, as stm32w_flasher cannot access different ports in parallel
|
||||
stm-reset:
|
||||
$(foreach PORT, $(MOTES), $(FLASHER) -r -p $(PORT);$(\n))
|
||||
@echo Done
|
||||
|
||||
ifdef MOTE
|
||||
%.upload: %.bin
|
||||
$(FLASHER) $(FLASHEROPTS) $< -p $(word $(MOTE), $(MOTES))
|
||||
else # MOTE
|
||||
%.upload: %.bin
|
||||
$(foreach PORT, $(MOTES), $(FLASHER) $(FLASHEROPTS) $< -p $(PORT);$(\n))
|
||||
endif # MOTE
|
||||
|
||||
ifdef MOTE
|
||||
login:
|
||||
$(SERIALDUMP) -b115200 -d10000 $(USBDEVPREFIX)$(word $(MOTE), $(MOTES))
|
||||
else
|
||||
login:
|
||||
$(SERIALDUMP) -b115200 -d10000 $(USBDEVPREFIX)$(firstword $(MOTES))
|
||||
endif
|
||||
|
@ -21,8 +21,8 @@ endif
|
||||
%.so: $(OBJECTDIR)/%.o
|
||||
$(LD) -shared -o $@ $^
|
||||
|
||||
# .PHONY: symbols.c symbols.h
|
||||
ifdef CORE
|
||||
.PHONY: symbols.c symbols.h
|
||||
symbols.c symbols.h:
|
||||
$(NM) $(CORE) | awk -f $(CONTIKI)/tools/mknmlist > symbols.c
|
||||
else
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/rime.h"
|
||||
#include "net/netstack.h"
|
||||
|
||||
#include "dev/leds.h"
|
||||
#include "dev/cc2420.h"
|
||||
@ -81,7 +82,7 @@ PROCESS_THREAD(scanner_process, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
/* switch mac layer off, and turn radio on */
|
||||
rime_mac->off(0);
|
||||
NETSTACK_MAC.off(0);
|
||||
cc2420_on();
|
||||
|
||||
while(1) {
|
||||
|
@ -39,6 +39,7 @@
|
||||
*/
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/netstack.h"
|
||||
#include "net/rime.h"
|
||||
#include "net/rime/collect.h"
|
||||
#include "net/rime/collect-neighbor.h"
|
||||
@ -133,7 +134,7 @@ do_rssi(void)
|
||||
static int sample;
|
||||
int channel;
|
||||
|
||||
rime_mac->off(0);
|
||||
NETSTACK_MAC.off(0);
|
||||
|
||||
cc2420_on();
|
||||
for(channel = 11; channel <= 26; ++channel) {
|
||||
@ -141,7 +142,7 @@ do_rssi(void)
|
||||
rssi_samples[sample].channel[channel - 11] = cc2420_rssi() + 53;
|
||||
}
|
||||
|
||||
rime_mac->on();
|
||||
NETSTACK_MAC.on();
|
||||
|
||||
sample = (sample + 1) % NUM_SAMPLES;
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "contiki.h"
|
||||
#include "net/rime.h"
|
||||
#include "net/netstack.h"
|
||||
|
||||
#include "dev/leds.h"
|
||||
#include "dev/cc2420.h"
|
||||
@ -83,7 +84,7 @@ PROCESS_THREAD(scanner_process, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
/* switch mac layer off, and turn radio on */
|
||||
rime_mac->off(0);
|
||||
NETSTACK_MAC.off(0);
|
||||
cc2420_on();
|
||||
|
||||
while(1) {
|
||||
|
@ -22,8 +22,3 @@ SERIALDUMP = $(CONTIKI)/tools/stm32w/serialdump-linux
|
||||
ifeq ($(HOST_OS),Windows)
|
||||
SERIALDUMP = $(CONTIKI)/tools/stm32w/serialdump-windows
|
||||
endif
|
||||
|
||||
|
||||
|
||||
login:
|
||||
$(SERIALDUMP) -b115200 -d10000 $(PORT)
|
||||
|
@ -172,6 +172,18 @@ main(int argc, char **argv)
|
||||
contiki_argc = argc;
|
||||
contiki_argv = argv;
|
||||
|
||||
/* native under windows is hardcoded to use the first one or two args */
|
||||
/* for wpcap configuration so this needs to be "removed" from */
|
||||
/* contiki_args (used by the native-border-router) */
|
||||
#ifdef __CYGWIN__
|
||||
contiki_argc--;
|
||||
contiki_argv++;
|
||||
#ifdef UIP_FALLBACK_INTERFACE
|
||||
contiki_argc--;
|
||||
contiki_argv++;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
process_init();
|
||||
process_start(&etimer_process, NULL);
|
||||
ctimer_init();
|
||||
|
@ -36,7 +36,6 @@ import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Observable;
|
||||
|
||||
@ -582,9 +581,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc
|
||||
/* Match file */
|
||||
Hashtable<Integer, Integer> lineTable = debuggingInfo.get(file);
|
||||
if (lineTable == null) {
|
||||
Enumeration<File> fileEnum = debuggingInfo.keys();
|
||||
while (fileEnum.hasMoreElements()) {
|
||||
File f = fileEnum.nextElement();
|
||||
for (File f: debuggingInfo.keySet()) {
|
||||
if (f != null && f.getName().equals(file.getName())) {
|
||||
lineTable = debuggingInfo.get(f);
|
||||
break;
|
||||
@ -598,9 +595,7 @@ public abstract class MspMote extends AbstractEmulatedMote implements Mote, Watc
|
||||
/* Match line number */
|
||||
Integer address = lineTable.get(lineNr);
|
||||
if (address != null) {
|
||||
Enumeration<Integer> lineEnum = lineTable.keys();
|
||||
while (lineEnum.hasMoreElements()) {
|
||||
Integer l = lineEnum.nextElement();
|
||||
for (Integer l: lineTable.keySet()) {
|
||||
if (l != null && l.intValue() == lineNr) {
|
||||
/* Found line address */
|
||||
return lineTable.get(l);
|
||||
|
@ -317,7 +317,7 @@ public abstract class MspMoteType implements MoteType {
|
||||
logger.warn("Old simulation config detected: SkySerial was replaced by MspSerial");
|
||||
intfClass = MspSerial.class.getName();
|
||||
}
|
||||
|
||||
|
||||
Class<? extends MoteInterface> moteInterfaceClass =
|
||||
simulation.getGUI().tryLoadClass(this, MoteInterface.class, intfClass);
|
||||
|
||||
@ -368,7 +368,7 @@ public abstract class MspMoteType implements MoteType {
|
||||
private static ELF loadELF(String filepath) throws IOException {
|
||||
return ELF.readELF(filepath);
|
||||
}
|
||||
|
||||
|
||||
private ELF elf; /* cached */
|
||||
public ELF getELF() throws IOException {
|
||||
if (elf == null) {
|
||||
@ -379,9 +379,9 @@ public abstract class MspMoteType implements MoteType {
|
||||
}
|
||||
return elf;
|
||||
}
|
||||
|
||||
|
||||
private Hashtable<File, Hashtable<Integer, Integer>> debuggingInfo = null; /* cached */
|
||||
public Hashtable<File, Hashtable<Integer, Integer>> getFirmwareDebugInfo()
|
||||
public Hashtable<File, Hashtable<Integer, Integer>> getFirmwareDebugInfo()
|
||||
throws IOException {
|
||||
if (debuggingInfo == null) {
|
||||
debuggingInfo = getFirmwareDebugInfo(getELF());
|
||||
@ -407,33 +407,35 @@ public abstract class MspMoteType implements MoteType {
|
||||
|
||||
for (int address: addresses) {
|
||||
DebugInfo info = elf.getDebugInfo(address);
|
||||
|
||||
if (info != null && info.getPath() != null && info.getFile() != null && info.getLine() >= 0) {
|
||||
|
||||
/* Nasty Cygwin-Windows fix */
|
||||
String path = info.getPath();
|
||||
if (path.contains("/cygdrive/")) {
|
||||
int index = path.indexOf("/cygdrive/");
|
||||
char driveCharacter = path.charAt(index+10);
|
||||
|
||||
path = path.replace("/cygdrive/" + driveCharacter + "/", driveCharacter + ":/");
|
||||
}
|
||||
|
||||
File file = new File(path, info.getFile());
|
||||
try {
|
||||
file = file.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
} catch (java.security.AccessControlException e) {
|
||||
}
|
||||
|
||||
Hashtable<Integer, Integer> lineToAddrHash = fileToLineHash.get(file);
|
||||
if (lineToAddrHash == null) {
|
||||
lineToAddrHash = new Hashtable<Integer, Integer>();
|
||||
fileToLineHash.put(file, lineToAddrHash);
|
||||
}
|
||||
|
||||
lineToAddrHash.put(info.getLine(), address);
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
if (info.getPath() == null && info.getFile() == null) {
|
||||
continue;
|
||||
}
|
||||
if (info.getLine() < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
File file;
|
||||
if (info.getPath() != null) {
|
||||
file = new File(info.getPath(), info.getFile());
|
||||
} else {
|
||||
file = new File(info.getFile());
|
||||
}
|
||||
try {
|
||||
file = file.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
} catch (java.security.AccessControlException e) {
|
||||
}
|
||||
|
||||
Hashtable<Integer, Integer> lineToAddrHash = fileToLineHash.get(file);
|
||||
if (lineToAddrHash == null) {
|
||||
lineToAddrHash = new Hashtable<Integer, Integer>();
|
||||
fileToLineHash.put(file, lineToAddrHash);
|
||||
}
|
||||
|
||||
lineToAddrHash.put(info.getLine(), address);
|
||||
}
|
||||
|
||||
return fileToLineHash;
|
||||
|
@ -163,6 +163,8 @@ public class CodeUI extends JPanel {
|
||||
}
|
||||
|
||||
/* Configure breakpoint menu options */
|
||||
/* XXX TODO We should ask for the file specified in the firmware, not
|
||||
* the actual file on disk. */
|
||||
Integer address =
|
||||
CodeUI.this.mote.getExecutableAddressOf(displayedFile, line);
|
||||
if (address == null) {
|
||||
@ -254,7 +256,8 @@ public class CodeUI extends JPanel {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
displayedFile = null;
|
||||
codeEditor.setText(null);
|
||||
codeEditor.setText("[no source displayed]");
|
||||
codeEditor.setEnabled(false);
|
||||
codeEditorLines.clear();
|
||||
displayLine(-1, markCurrent);
|
||||
}
|
||||
@ -275,6 +278,7 @@ public class CodeUI extends JPanel {
|
||||
displayNoCode(markCurrent);
|
||||
return;
|
||||
}
|
||||
codeEditor.setEnabled(true);
|
||||
|
||||
String[] lines = data.split("\n");
|
||||
logger.info("Opening " + codeFile + " (" + lines.length + " lines)");
|
||||
|
@ -32,12 +32,15 @@ package se.sics.cooja.mspmote.plugins;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
@ -47,23 +50,23 @@ import javax.swing.Box;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jdom.Element;
|
||||
|
||||
import se.sics.cooja.ClassDescription;
|
||||
import se.sics.cooja.GUI;
|
||||
import se.sics.cooja.GUI.RunnableInEDT;
|
||||
import se.sics.cooja.Mote;
|
||||
import se.sics.cooja.MotePlugin;
|
||||
import se.sics.cooja.PluginType;
|
||||
@ -72,6 +75,7 @@ import se.sics.cooja.VisPlugin;
|
||||
import se.sics.cooja.Watchpoint;
|
||||
import se.sics.cooja.WatchpointMote;
|
||||
import se.sics.cooja.WatchpointMote.WatchpointListener;
|
||||
import se.sics.cooja.dialogs.MessageList;
|
||||
import se.sics.cooja.mspmote.MspMote;
|
||||
import se.sics.cooja.mspmote.MspMoteType;
|
||||
import se.sics.mspsim.core.EmulationException;
|
||||
@ -103,11 +107,14 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
private WatchpointListener watchpointListener;
|
||||
|
||||
private JComboBox fileComboBox;
|
||||
private String[] debugInfoMap = null;
|
||||
private File[] sourceFiles;
|
||||
|
||||
private JTabbedPane mainPane;
|
||||
|
||||
private ArrayList<Rule> rules;
|
||||
private ELFDebug debug;
|
||||
private String[] debugSourceFiles;
|
||||
|
||||
/**
|
||||
* Mini-debugger for MSP Motes.
|
||||
* Visualizes instructions, source code and allows a user to manipulate breakpoints.
|
||||
@ -121,6 +128,41 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
simulation = simulationToVisualize;
|
||||
this.mspMote = (MspMote) mote;
|
||||
this.watchpointMote = (WatchpointMote) mote;
|
||||
try {
|
||||
debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
if (debug == null) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
debugSourceFiles = debug.getSourceFiles();
|
||||
if (debugSourceFiles == null) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
throw new RuntimeException("No debugging info found in firmware, aborting");
|
||||
}
|
||||
|
||||
/* XXX Temporary workaround: source file removing duplicates awaiting Mspsim update */
|
||||
{
|
||||
ArrayList<String> newDebugSourceFiles = new ArrayList<String>();
|
||||
for (String sf: debugSourceFiles) {
|
||||
boolean found = false;
|
||||
for (String nsf: newDebugSourceFiles) {
|
||||
if (sf.equals(nsf)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
newDebugSourceFiles.add(sf);
|
||||
}
|
||||
}
|
||||
debugSourceFiles = newDebugSourceFiles.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
||||
rules = new ArrayList<Rule>();
|
||||
|
||||
loadDefaultRules();
|
||||
|
||||
getContentPane().setLayout(new BorderLayout());
|
||||
|
||||
@ -150,7 +192,6 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
return this;
|
||||
}
|
||||
});
|
||||
updateFileComboBox();
|
||||
|
||||
/* Browse code control (north) */
|
||||
Box sourceCodeControl = Box.createHorizontalBox();
|
||||
@ -223,8 +264,13 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
showCurrentPC();
|
||||
}
|
||||
|
||||
public void startPlugin() {
|
||||
super.startPlugin();
|
||||
updateFileComboBox();
|
||||
}
|
||||
|
||||
private void updateFileComboBox() {
|
||||
sourceFiles = getSourceFiles(mspMote, debugInfoMap);
|
||||
sourceFiles = getSourceFiles(mspMote, rules);
|
||||
fileComboBox.removeAllItems();
|
||||
fileComboBox.addItem("[view sourcefile]");
|
||||
for (File f: sourceFiles) {
|
||||
@ -288,39 +334,22 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
currentCodeFile = null;
|
||||
|
||||
try {
|
||||
ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
if (debug == null) {
|
||||
return;
|
||||
}
|
||||
int pc = mspMote.getCPU().getPC();
|
||||
DebugInfo debugInfo = debug.getDebugInfo(pc);
|
||||
if (pc <= 0) {
|
||||
return;
|
||||
}
|
||||
if (debugInfo == null) {
|
||||
if (pc != 0) {
|
||||
logger.warn("No sourcecode info at " + String.format("0x%04x", mspMote.getCPU().getPC()));
|
||||
}
|
||||
logger.warn("No source info at " + String.format("0x%04x", pc));
|
||||
return;
|
||||
}
|
||||
File f = applySubstitutionRules(new File(debugInfo.getPath(), debugInfo.getFile()), rules);
|
||||
if (f == null || !f.exists()) {
|
||||
logger.warn("Unknown source at " + String.format("0x%04x", pc) + ": " + debugInfo.getPath() + " " + debugInfo.getFile());
|
||||
return;
|
||||
}
|
||||
|
||||
String path = new File(debugInfo.getPath(), debugInfo.getFile()).getPath();
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
path = path.replace('\\', '/');
|
||||
|
||||
/* Debug info to source file map */
|
||||
if (debugInfoMap != null && debugInfoMap.length == 2) {
|
||||
if (path.startsWith(debugInfoMap[0])) {
|
||||
path = debugInfoMap[1] + path.substring(debugInfoMap[0].length());
|
||||
}
|
||||
}
|
||||
|
||||
/* Nasty Cygwin-Windows fix */
|
||||
if (path.length() > 10 && path.startsWith("/cygdrive/")) {
|
||||
char driveCharacter = path.charAt(10);
|
||||
path = driveCharacter + ":/" + path.substring(11);
|
||||
}
|
||||
|
||||
currentCodeFile = new File(path).getCanonicalFile();
|
||||
currentCodeFile = f;
|
||||
currentLineNumber = debugInfo.getLine();
|
||||
} catch (Exception e) {
|
||||
logger.fatal("Exception: " + e.getMessage(), e);
|
||||
@ -329,147 +358,332 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void tryMapDebugInfo() {
|
||||
final String[] debugFiles;
|
||||
try {
|
||||
|
||||
ELFDebug debug = ((MspMoteType)mspMote.getType()).getELF().getDebug();
|
||||
debugFiles = debug != null ? debug.getSourceFiles() : null;
|
||||
if (debugFiles == null) {
|
||||
logger.fatal("Error: No debug information is available");
|
||||
return;
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
logger.fatal("Error: " + e1.getMessage(), e1);
|
||||
return;
|
||||
private int getLocatedSourcesCount() {
|
||||
File files[] = getSourceFiles(mspMote, rules);
|
||||
if (files == null) {
|
||||
return 0;
|
||||
}
|
||||
String[] map = new RunnableInEDT<String[]>() {
|
||||
public String[] work() {
|
||||
/* Select which source file to use */
|
||||
int counter = 0, n;
|
||||
File correspondingFile = null;
|
||||
while (true) {
|
||||
n = JOptionPane.showOptionDialog(GUI.getTopParentContainer(),
|
||||
"Choose which source file to manually locate.\n\n" +
|
||||
"Some source files may not exist, as debug info is also inherited from the toolchain.\n" +
|
||||
"\"Next File\" proceeds to the next source file in the debug info.\n\n" +
|
||||
debugFiles[counter] + " (" + (counter+1) + '/' + debugFiles.length + ')',
|
||||
"Select source file to locate", JOptionPane.YES_NO_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE, null,
|
||||
new String[] { "Next File", "Locate File", "Cancel"}, "Next File");
|
||||
if (n == JOptionPane.CANCEL_OPTION || n == JOptionPane.CLOSED_OPTION) {
|
||||
return null;
|
||||
}
|
||||
if (n == JOptionPane.NO_OPTION) {
|
||||
/* Locate file */
|
||||
final String filename = new File(debugFiles[counter]).getName();
|
||||
JFileChooser fc = new JFileChooser();
|
||||
fc.setFileFilter(new FileFilter() {
|
||||
public boolean accept(File file) {
|
||||
if (file.isDirectory()) { return true; }
|
||||
if (file.getName().equals(filename)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public String getDescription() {
|
||||
return "Source file " + filename;
|
||||
}
|
||||
});
|
||||
fc.setCurrentDirectory(new File(GUI.getExternalToolsSetting("PATH_CONTIKI", ".")));
|
||||
int returnVal = fc.showOpenDialog(GUI.getTopParentContainer());
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
correspondingFile = fc.getSelectedFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return files.length;
|
||||
}
|
||||
|
||||
if (n == JOptionPane.YES_OPTION) {
|
||||
/* Next file */
|
||||
counter = (counter+1) % debugFiles.length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Match files */
|
||||
try {
|
||||
String canonDebug = debugFiles[counter];
|
||||
String canonSelected = correspondingFile.getCanonicalFile().getPath().replace('\\', '/');
|
||||
|
||||
int offset = 0;
|
||||
while (canonDebug.regionMatches(
|
||||
true,
|
||||
canonDebug.length()-offset,
|
||||
canonSelected, canonSelected.length()-offset,
|
||||
offset)) {
|
||||
offset++;
|
||||
if (offset >= canonDebug.length() ||
|
||||
offset >= canonSelected.length())
|
||||
break;
|
||||
}
|
||||
offset--;
|
||||
String replace = canonDebug.substring(0, canonDebug.length() - offset);
|
||||
String replacement = canonSelected.substring(0, canonSelected.length() - offset);
|
||||
|
||||
{
|
||||
JTextField replaceInput = new JTextField(replace);
|
||||
replaceInput.setEditable(true);
|
||||
JTextField replacementInput = new JTextField(replacement);
|
||||
replacementInput.setEditable(true);
|
||||
|
||||
Box box = Box.createVerticalBox();
|
||||
box.add(new JLabel("Debug info file:"));
|
||||
box.add(new JLabel(canonDebug));
|
||||
box.add(new JLabel("Selected file:"));
|
||||
box.add(new JLabel(canonSelected));
|
||||
box.add(Box.createVerticalStrut(20));
|
||||
box.add(new JLabel("Replacing:"));
|
||||
box.add(replaceInput);
|
||||
box.add(new JLabel("with:"));
|
||||
box.add(replacementInput);
|
||||
|
||||
JOptionPane optionPane = new JOptionPane();
|
||||
optionPane.setMessage(box);
|
||||
optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
|
||||
optionPane.setOptions(new String[] { "OK" });
|
||||
optionPane.setInitialValue("OK");
|
||||
JDialog dialog = optionPane.createDialog(
|
||||
GUI.getTopParentContainer(),
|
||||
"Mapping debug info to real sources");
|
||||
dialog.setVisible(true);
|
||||
|
||||
replace = replaceInput.getText();
|
||||
replacement = replacementInput.getText();
|
||||
}
|
||||
|
||||
replace = replace.replace('\\', '/');
|
||||
replacement = replacement.replace('\\', '/');
|
||||
return new String[] { replace, replacement };
|
||||
} catch (IOException e) {
|
||||
logger.fatal("Error: " + e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}.invokeAndWait();
|
||||
|
||||
if (map != null) {
|
||||
debugInfoMap = map;
|
||||
updateFileComboBox();
|
||||
private void updateRulesUsage() {
|
||||
for (Rule rule: rules) {
|
||||
rule.prefixMatches = 0;
|
||||
rule.locatesFile = 0;
|
||||
}
|
||||
getSourceFiles(mspMote, rules);
|
||||
rulesMatched = new int[rules.size()];
|
||||
rulesOK = new int[rules.size()];
|
||||
for (int i=0; i < rules.size(); i++) {
|
||||
rulesMatched[i] = rules.get(i).prefixMatches;
|
||||
rulesOK[i] = rules.get(i).locatesFile;
|
||||
}
|
||||
}
|
||||
|
||||
private static File[] getSourceFiles(MspMote mote, String[] map) {
|
||||
final String[] sourceFiles;
|
||||
try {
|
||||
ELFDebug debug = ((MspMoteType)mote.getType()).getELF().getDebug();
|
||||
sourceFiles = debug != null ? debug.getSourceFiles() : null;
|
||||
if (sourceFiles == null) {
|
||||
logger.fatal("Error: No debug information is available");
|
||||
return new File[0];
|
||||
private class Rule {
|
||||
String from = "";
|
||||
String to = "";
|
||||
int prefixMatches = 0;
|
||||
int locatesFile = 0;
|
||||
public Rule(String from, String to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
File apply(File f) {
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
logger.fatal("Error: " + e1.getMessage(), e1);
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
if (!f.getPath().startsWith(from)) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " does not match: " + f.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
prefixMatches++;
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " testing on: " + f.getPath());
|
||||
}
|
||||
if (to == null) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " enter substition: " + f.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
File file = new File(to + f.getPath().substring(from.length()));
|
||||
if (!file.exists()) {
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " not found: " + file.getPath(), MessageList.ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (rulesWithDebuggingOutput) {
|
||||
rulesDebuggingOutput.addMessage(this + " OK: " + f.getPath());
|
||||
}
|
||||
locatesFile++;
|
||||
return file;
|
||||
}
|
||||
public String toString() {
|
||||
return "[" + from + "|" + to + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private static File applySubstitutionRules(String file, Collection<Rule> rules) {
|
||||
return applySubstitutionRules(new File(file), rules);
|
||||
}
|
||||
private static File applySubstitutionRules(File file, Collection<Rule> rules) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
for (Rule rule: rules) {
|
||||
File f = rule.apply(file);
|
||||
if (f != null && f.exists()) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void loadDefaultRules() {
|
||||
String rulesString = GUI.getExternalToolsSetting("MSPCODEWATCHER_RULES", "/cygdrive/c/*c:/");
|
||||
String[] rulesArr = rulesString.split("\\*");
|
||||
rules.clear();
|
||||
for (int i=0; i < rulesArr.length/2; i++) {
|
||||
Rule r = new Rule(rulesArr[i*2], rulesArr[i*2+1]);
|
||||
if (r.from.equals("[empty]")) {
|
||||
r.from = "";
|
||||
}
|
||||
if (r.to.equals("[empty]")) {
|
||||
r.to = "";
|
||||
}
|
||||
rules.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageList rulesDebuggingOutput = new MessageList();
|
||||
private boolean rulesWithDebuggingOutput = false;
|
||||
private int[] rulesMatched = null;
|
||||
private int[] rulesOK = null;
|
||||
private JTable table = null;
|
||||
private void tryMapDebugInfo() {
|
||||
/* called from AWT */
|
||||
int r = JOptionPane.showConfirmDialog(GUI.getTopParentContainer(),
|
||||
"The firmware file " + mspMote.getType().getContikiFirmwareFile().getName() + " references " + debugSourceFiles.length + " source files.\n" +
|
||||
"This function tries to locate these files on disk with a set of simple substitution rules.\n" +
|
||||
"\n" +
|
||||
"Right now " + getLocatedSourcesCount() + "/" + debugSourceFiles.length + " source files can be found.",
|
||||
"Locate source files", JOptionPane.OK_CANCEL_OPTION);
|
||||
if (r != JOptionPane.OK_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* table with rules */
|
||||
rulesDebuggingOutput.clearMessages();
|
||||
final JDialog dialog = new JDialog((Window)GUI.getTopParentContainer(), "Locate source files");
|
||||
dialog.setModal(true);
|
||||
updateRulesUsage();
|
||||
AbstractTableModel model = new AbstractTableModel() {
|
||||
public int getRowCount() {
|
||||
return 10;
|
||||
}
|
||||
public int getColumnCount() {
|
||||
return 4;
|
||||
}
|
||||
public String getColumnName(int col) {
|
||||
if (col == 0) {
|
||||
return "Prefix";
|
||||
}
|
||||
if (col == 1) {
|
||||
return "Substitution";
|
||||
}
|
||||
if (col == 2) {
|
||||
return "Matched";
|
||||
}
|
||||
if (col == 3) {
|
||||
return "OK";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
if (rowIndex < 0 || rowIndex >= rules.size()) {
|
||||
return null;
|
||||
}
|
||||
Rule rule = rules.get(rowIndex);
|
||||
if (columnIndex == 0) {
|
||||
return rule.from;
|
||||
}
|
||||
if (columnIndex == 1) {
|
||||
return rule.to;
|
||||
}
|
||||
if (columnIndex == 2) {
|
||||
if (rulesMatched == null || rowIndex >= rulesMatched.length) {
|
||||
return "[click Apply]";
|
||||
}
|
||||
return rulesMatched[rowIndex];
|
||||
}
|
||||
if (columnIndex == 3) {
|
||||
if (rulesOK == null || rowIndex >= rulesOK.length) {
|
||||
return "[click Apply]";
|
||||
}
|
||||
return rulesOK[rowIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
if (column == 0) {
|
||||
return true;
|
||||
}
|
||||
if (column == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
Rule rule;
|
||||
if (rowIndex < 0) {
|
||||
return;
|
||||
}
|
||||
if (rowIndex < rules.size()) {
|
||||
rule = rules.get(rowIndex);
|
||||
} else {
|
||||
do {
|
||||
rule = new Rule("", "");
|
||||
rules.add(rule);
|
||||
} while (rowIndex >= rules.size());
|
||||
}
|
||||
if (columnIndex == 0) {
|
||||
rule.from = (String) aValue;
|
||||
}
|
||||
if (columnIndex == 1) {
|
||||
rule.to = (String) aValue;
|
||||
}
|
||||
rulesMatched = null;
|
||||
rulesOK = null;
|
||||
table.invalidate();
|
||||
table.repaint();
|
||||
}
|
||||
};
|
||||
table = new JTable(model);
|
||||
|
||||
/* control panel: save/load, clear/apply/close */
|
||||
final JButton applyButton = new JButton("Apply");
|
||||
applyButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
/* Remove trailing empty rules */
|
||||
ArrayList<Rule> trimmedRules = new ArrayList<Rule>();
|
||||
for (Rule rule: rules) {
|
||||
if (rule.from == null || rule.from.trim().isEmpty()) {
|
||||
rule.from = "";
|
||||
}
|
||||
if (rule.to == null || rule.to.trim().isEmpty()) {
|
||||
rule.to = "";
|
||||
}
|
||||
if (rule.from.isEmpty() && rule.to.isEmpty()) {
|
||||
/* ignore */
|
||||
continue;
|
||||
}
|
||||
trimmedRules.add(rule);
|
||||
}
|
||||
rules = trimmedRules;
|
||||
|
||||
rulesDebuggingOutput.clearMessages();
|
||||
rulesDebuggingOutput.addMessage("Applying " + rules.size() + " substitution rules");
|
||||
rulesWithDebuggingOutput = true;
|
||||
updateRulesUsage();
|
||||
rulesWithDebuggingOutput = false;
|
||||
rulesDebuggingOutput.addMessage("Done! " + "Located sources: " + getLocatedSourcesCount() + "/" + debugSourceFiles.length);
|
||||
rulesDebuggingOutput.addMessage(" ");
|
||||
for (String s: debugSourceFiles) {
|
||||
File f = applySubstitutionRules(s, rules);
|
||||
if (f == null || !f.exists()) {
|
||||
rulesDebuggingOutput.addMessage("Not yet located: " + s, MessageList.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
table.invalidate();
|
||||
table.repaint();
|
||||
}
|
||||
});
|
||||
JButton clearButton = new JButton("Clear");
|
||||
clearButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
rules.clear();
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
|
||||
JButton loadButton = new JButton("Load default");
|
||||
loadButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
loadDefaultRules();
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
JButton saveButton = new JButton("Save as default");
|
||||
saveButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Rule r: rules) {
|
||||
sb.append("*");
|
||||
if (r.from.isEmpty()) {
|
||||
sb.append("[empty]");
|
||||
} else {
|
||||
sb.append(r.from);
|
||||
}
|
||||
sb.append("*");
|
||||
if (r.to.isEmpty()) {
|
||||
sb.append("[empty]");
|
||||
} else {
|
||||
sb.append(r.to);
|
||||
}
|
||||
}
|
||||
if (sb.length() >= 1) {
|
||||
GUI.setExternalToolsSetting("MSPCODEWATCHER_RULES", sb.substring(1));
|
||||
} else {
|
||||
GUI.setExternalToolsSetting("MSPCODEWATCHER_RULES", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JButton closeButton = new JButton("Close");
|
||||
closeButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateFileComboBox();
|
||||
dialog.dispose();
|
||||
}
|
||||
});
|
||||
Box control = Box.createHorizontalBox();
|
||||
control.add(loadButton);
|
||||
control.add(saveButton);
|
||||
control.add(Box.createHorizontalGlue());
|
||||
control.add(clearButton);
|
||||
control.add(applyButton);
|
||||
control.add(closeButton);
|
||||
|
||||
final JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
|
||||
new JScrollPane(table),
|
||||
new JScrollPane(rulesDebuggingOutput));
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
split.setDividerLocation(0.5);
|
||||
applyButton.doClick();
|
||||
}
|
||||
});
|
||||
dialog.getContentPane().add(BorderLayout.CENTER, split);
|
||||
dialog.getContentPane().add(BorderLayout.SOUTH, control);
|
||||
dialog.getRootPane().setDefaultButton(closeButton);
|
||||
dialog.setSize(550, 500);
|
||||
dialog.setLocationRelativeTo(GUI.getTopParentContainer());
|
||||
dialog.setVisible(true);
|
||||
}
|
||||
|
||||
private File[] getSourceFiles(MspMote mote, ArrayList<Rule> rules) {
|
||||
File contikiSource = mote.getType().getContikiSourceFile();
|
||||
if (contikiSource != null) {
|
||||
try {
|
||||
@ -480,75 +694,22 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
|
||||
/* Verify that files exist */
|
||||
ArrayList<File> existing = new ArrayList<File>();
|
||||
for (String sourceFile: sourceFiles) {
|
||||
|
||||
for (String sourceFile: debugSourceFiles) {
|
||||
/* Debug info to source file map */
|
||||
sourceFile = sourceFile.replace('\\', '/');
|
||||
if (map != null && map.length == 2) {
|
||||
if (sourceFile.startsWith(map[0])) {
|
||||
sourceFile = map[1] + sourceFile.substring(map[0].length());
|
||||
}
|
||||
File f = applySubstitutionRules(sourceFile, rules);
|
||||
if (f != null && f.exists()) {
|
||||
existing.add(f);
|
||||
}
|
||||
|
||||
/* Nasty Cygwin-Windows fix */
|
||||
if (sourceFile.length() > 10 && sourceFile.contains("/cygdrive/")) {
|
||||
char driveCharacter = sourceFile.charAt(10);
|
||||
sourceFile = driveCharacter + ":/" + sourceFile.substring(11);
|
||||
}
|
||||
|
||||
File file = new File(sourceFile);
|
||||
try {
|
||||
file = file.getCanonicalFile();
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
if (!GUI.isVisualizedInApplet()) {
|
||||
if (file.exists() && file.isFile()) {
|
||||
existing.add(file);
|
||||
} else {
|
||||
/*logger.warn("Can't locate source file, skipping: " + file.getPath());*/
|
||||
}
|
||||
} else {
|
||||
/* Accept all files without existence check */
|
||||
existing.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
/* If no files were found, suggest map function */
|
||||
if (sourceFiles.length > 0 && existing.isEmpty() && GUI.isVisualized()) {
|
||||
new RunnableInEDT<Boolean>() {
|
||||
public Boolean work() {
|
||||
JOptionPane.showMessageDialog(
|
||||
GUI.getTopParentContainer(),
|
||||
"The firmware debug info specifies " + sourceFiles.length + " source files.\n" +
|
||||
"However, Msp Code Watcher could not find any of these files.\n" +
|
||||
"Make sure the source files were not moved after the firmware compilation.\n" +
|
||||
"\n" +
|
||||
"If you want to manually locate the sources, click \"Map\" button.",
|
||||
"No source files found",
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
return true;
|
||||
}
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
/* Sort alphabetically */
|
||||
ArrayList<File> sorted = new ArrayList<File>();
|
||||
for (File file: existing) {
|
||||
int index = 0;
|
||||
for (index=0; index < sorted.size(); index++) {
|
||||
if (file.getName().compareToIgnoreCase(sorted.get(index).getName()) < 0) {
|
||||
break;
|
||||
}
|
||||
File[] sorted = existing.toArray(new File[0]);
|
||||
Arrays.sort(sorted, new Comparator<File>() {
|
||||
public int compare(File o1, File o2) {
|
||||
return o1.getName().compareToIgnoreCase(o2.getName());
|
||||
}
|
||||
sorted.add(index, file);
|
||||
}
|
||||
|
||||
/* Add Contiki source first */
|
||||
if (contikiSource != null && contikiSource.exists()) {
|
||||
sorted.add(0, contikiSource);
|
||||
}
|
||||
|
||||
return sorted.toArray(new File[0]);
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
|
||||
public Collection<Element> getConfigXML() {
|
||||
@ -559,13 +720,27 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
element.addContent("" + mainPane.getSelectedIndex());
|
||||
config.add(element);
|
||||
|
||||
for (Rule rule: rules) {
|
||||
element = new Element("rule");
|
||||
element.setAttribute("from", rule.from==null?"":rule.from);
|
||||
element.setAttribute("to", rule.to==null?"":rule.to);
|
||||
config.add(element);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
|
||||
boolean clearedRules = false;
|
||||
for (Element element : configXML) {
|
||||
if (element.getName().equals("tab")) {
|
||||
mainPane.setSelectedIndex(Integer.parseInt(element.getText()));
|
||||
} else if (element.getName().equals("rule")) {
|
||||
if (!clearedRules) {
|
||||
rules.clear();
|
||||
clearedRules = true;
|
||||
}
|
||||
rules.add(new Rule(element.getAttributeValue("from"), element.getAttributeValue("to")));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -582,7 +757,7 @@ public class MspCodeWatcher extends VisPlugin implements MotePlugin {
|
||||
}
|
||||
};
|
||||
|
||||
private AbstractAction mapAction = new AbstractAction("Locate sources") {
|
||||
private AbstractAction mapAction = new AbstractAction("Locate sources...") {
|
||||
private static final long serialVersionUID = -3929432830596292495L;
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
|
@ -2943,10 +2943,24 @@ public class GUI extends Observable {
|
||||
* null
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
String logConfigFile = null;
|
||||
for (String element : args) {
|
||||
if (element.startsWith("-log4j=")) {
|
||||
String arg = element.substring("-log4j=".length());
|
||||
logConfigFile = arg;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Configure logger
|
||||
if ((new File(LOG_CONFIG_FILE)).exists()) {
|
||||
if (logConfigFile != null) {
|
||||
if (new File(logConfigFile).exists()) {
|
||||
DOMConfigurator.configure(logConfigFile);
|
||||
} else {
|
||||
System.err.println("Failed to open " + logConfigFile);
|
||||
System.exit(1);
|
||||
}
|
||||
} else if (new File(LOG_CONFIG_FILE).exists()) {
|
||||
DOMConfigurator.configure(LOG_CONFIG_FILE);
|
||||
} else {
|
||||
// Used when starting from jar
|
||||
|
@ -696,7 +696,6 @@ public class ScriptRunner extends VisPlugin {
|
||||
}
|
||||
});
|
||||
if (fileChooser.showOpenDialog(scriptRunner) != JFileChooser.APPROVE_OPTION) {
|
||||
logger.debug("cancel");
|
||||
return;
|
||||
}
|
||||
scriptRunner.setLinkFile(fileChooser.getSelectedFile());
|
||||
|
280
tools/stm32w/motelist-linux
Normal file
280
tools/stm32w/motelist-linux
Normal file
@ -0,0 +1,280 @@
|
||||
#!/usr/bin/perl -w
|
||||
use strict;
|
||||
# $Id: motelist-linux,v 1.1 2007/05/29 19:34:30 adam Exp $
|
||||
# @author Cory Sharp <cory@moteiv.com>
|
||||
# @author Joe Polastre
|
||||
|
||||
my $help = <<'EOF';
|
||||
usage: motelist [options]
|
||||
|
||||
$Revision: 1.1 $
|
||||
|
||||
options:
|
||||
-h display this help
|
||||
-c compact format, not pretty but easier for parsing
|
||||
-f specify the usb-serial file (for smote.cs)
|
||||
-k kernel version: 2.4, 2.6, auto (default)
|
||||
-m method to scan usb: procfs, sysfs, auto (default)
|
||||
-dev_prefix force the device prefix for the serial device
|
||||
-usb display extra usb information
|
||||
EOF
|
||||
|
||||
my %Opt = (
|
||||
compact => 0,
|
||||
usb => 0,
|
||||
method => "auto",
|
||||
kernel => "auto",
|
||||
dev_prefix => [ "/dev/usb/tts/", "/dev/ttyUSB", "/dev/tts/USB" ],
|
||||
usbserial => "sudo cat /proc/tty/driver/usbserial |",
|
||||
);
|
||||
|
||||
while (@ARGV) {
|
||||
last unless $ARGV[0] =~ /^-/;
|
||||
my $opt = shift @ARGV;
|
||||
if( $opt eq "-h" ) { print "$help\n"; exit 0; }
|
||||
elsif( $opt eq "-c" ) { $Opt{compact} = 1; }
|
||||
elsif( $opt eq "-f" ) { $Opt{usbserial} = shift @ARGV; }
|
||||
elsif( $opt eq "-k" ) { $Opt{kernel} = shift @ARGV; }
|
||||
elsif( $opt eq "-m" ) { $Opt{method} = shift @ARGV; }
|
||||
elsif( $opt eq "-dev_prefix" ) { $Opt{dev_prefix} = shift @ARGV; }
|
||||
elsif( $opt eq "-usb" ) { $Opt{usb} = 1; }
|
||||
else { print STDERR "$help\nerror, unknown command line option $opt\n"; exit 1; }
|
||||
}
|
||||
|
||||
if( $Opt{kernel} eq "auto" ) {
|
||||
$Opt{kernel} = "unknown";
|
||||
$Opt{kernel} = $1 if snarf("/proc/version") =~ /\bLinux version (\d+\.\d+)/;
|
||||
}
|
||||
|
||||
if( $Opt{method} eq "auto" ) {
|
||||
$Opt{method} = ($Opt{kernel} eq "2.4") ? "procfs" : "sysfs";
|
||||
}
|
||||
|
||||
my @devs = $Opt{method} eq "procfs" ? scan_procfs() : scan_sysfs();
|
||||
print_motelist( sort { cmp_usbdev($a,$b) } @devs );
|
||||
|
||||
|
||||
#
|
||||
# SysFS
|
||||
#
|
||||
sub scan_sysfs {
|
||||
|
||||
# Scan /sys/bus/usb/drivers/usb for FTDI devices
|
||||
my @ftdidevs =
|
||||
grep { ($_->{UsbVendor}||"") eq "0403" && ($_->{UsbProduct}||"") eq "6001" && ($_->{UsbBcdDevice}||"") eq "0600"}
|
||||
map { {
|
||||
SysPath => $_,
|
||||
UsbVendor => snarf("$_/idVendor",1),
|
||||
UsbProduct => snarf("$_/idProduct",1),
|
||||
UsbBcdDevice => snarf("$_/bcdDevice",1),
|
||||
} }
|
||||
glob("/sys/bus/usb/drivers/usb/*");
|
||||
|
||||
# Gather information about each FTDI device
|
||||
for my $f (@ftdidevs) {
|
||||
my $syspath = $f->{SysPath};
|
||||
|
||||
$f->{InfoManufacturer} = snarf("$syspath/manufacturer",1);
|
||||
$f->{InfoProduct} = snarf("$syspath/product",1);
|
||||
$f->{InfoSerial} = snarf("$syspath/serial",1);
|
||||
$f->{UsbDevNum} = snarf("$syspath/devnum",1);
|
||||
print $syspath;
|
||||
print "\n";
|
||||
|
||||
my $devstr = readlink($syspath);
|
||||
if( $devstr =~ m{([^/]+)/usb(\d+)/.*-([^/]+)$} ) {
|
||||
$f->{UsbPath} = "usb-$1-$3";
|
||||
$f->{UsbBusNum} = $2;
|
||||
}
|
||||
($f->{SysDev} = $syspath) =~ s{^.*/}{};
|
||||
|
||||
my $port = "$syspath/$f->{SysDev}:1.0";
|
||||
($f->{DriverName} = readlink("$port/driver")) =~ s{^.*/}{} if -l "$port/driver";
|
||||
($f->{SerialDevName} = (glob("$port/tty*"),undef)[0]) =~ s{^.*/}{};
|
||||
$f->{SerialDevNum} = $1 if $f->{SerialDevName} =~ /(\d+)/;
|
||||
$f->{SerialDevName} = getSerialDevName( $f->{SerialDevNum} ) || " (none)";
|
||||
}
|
||||
|
||||
return @ftdidevs;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Scan Procfs
|
||||
#
|
||||
sub scan_procfs {
|
||||
|
||||
my $text_devs = snarf("< /proc/bus/usb/devices");
|
||||
my $text_serial = snarf($Opt{usbserial});
|
||||
|
||||
my @usbdevs = map { {parse_usb_devices_text($_)} }
|
||||
grep { !/^\s*$/ } split /\n+(?=T:)/, $text_devs;
|
||||
my %usbtree = build_usb_tree( @usbdevs );
|
||||
my %usbserialtree = build_usbserial_tree( $text_serial );
|
||||
for my $tts ( values %usbserialtree ) {
|
||||
$usbtree{usbkey($tts->{path})}{usbserial} = $tts if defined $tts->{path};
|
||||
}
|
||||
|
||||
my @ftdidevs = map { {
|
||||
UsbVendor => $_->{Vendor},
|
||||
UsbProduct => $_->{ProdID},
|
||||
UsbBcdDevice => $_->{BcdDevice},
|
||||
InfoManufacturer => $_->{Manufacturer},
|
||||
InfoProduct => $_->{Product},
|
||||
InfoSerial => $_->{SerialNumber},
|
||||
UsbBusNum => $_->{nbus},
|
||||
UsbDevNum => $_->{ndev},
|
||||
UsbPath => (($Opt{kernel} eq "2.4") ? $_->{usbserial}{path} : $_->{usbpath}),
|
||||
DriverName => $_->{driver},
|
||||
SerialDevNum => $_->{usbserial}{tts},
|
||||
SerialDevName => getSerialDevName($_->{usbserial}{tts}) || " (none)",
|
||||
} }
|
||||
grep { ($_->{Vendor}||"") eq "0403" && ($_->{ProdID}||"") eq "6001" && ($_->{BcdDevice}||"") eq "0600"}
|
||||
values %usbtree;
|
||||
|
||||
return @ftdidevs;
|
||||
}
|
||||
|
||||
sub build_usb_tree {
|
||||
my @devs = @_;
|
||||
my %tree = ();
|
||||
for my $dev (sort { $a->{Lev} <=> $b->{Lev} } @devs) {
|
||||
my ($bus,$lev,$prnt) = ( $dev->{Bus}+0, $dev->{Lev}+0, $dev->{Prnt}+0 );
|
||||
my $devnum = $dev->{"Dev#"}+0;
|
||||
$dev->{nbus} = $bus;
|
||||
$dev->{ndev} = $devnum;
|
||||
$tree{"bus$bus"} = {} unless exists $tree{"bus$bus"};
|
||||
$tree{"bus$bus"}{"dev$devnum"} = $dev;
|
||||
if( $lev == 0 ) {
|
||||
$dev->{usbpath} = "usb-$dev->{SerialNumber}";
|
||||
} else {
|
||||
my $sep = ($lev==1) ? "-" : ".";
|
||||
$dev->{parent} = $tree{"bus$bus"}{"dev$prnt"};
|
||||
$dev->{usbpath} = $dev->{parent}{usbpath} . $sep . ($dev->{Port}+1);
|
||||
}
|
||||
$tree{usbkey($dev->{usbpath})} = $dev;
|
||||
}
|
||||
return %tree;
|
||||
}
|
||||
|
||||
sub parse_usb_devices_text {
|
||||
my $text = shift;
|
||||
$text =~ s/^\S+\s*//gm;
|
||||
$text =~ s/\s*(\S+=)/\001$1/g;
|
||||
return map { m/(.*?)=(.*)/ } split /\001/, $text;
|
||||
}
|
||||
|
||||
sub build_usbserial_tree {
|
||||
my $text = shift;
|
||||
my %tree = ();
|
||||
while( $text =~ /^([^:]+):(.*)/mg ) {
|
||||
my ($tts,$params) = ($1,$2);
|
||||
$tree{$tts} = { tts => $tts };
|
||||
while ($params =~ m/\s+([^:]+):(?:"([^"]*)"|(\S+))/g) {
|
||||
$tree{$tts}{$1} = $2||$3;
|
||||
}
|
||||
}
|
||||
return %tree;
|
||||
}
|
||||
|
||||
sub usbkey {
|
||||
if( $Opt{kernel} eq "2.4" ) {
|
||||
(my $key = $_[0]) =~ s/^.*-//;
|
||||
return $key;
|
||||
}
|
||||
return $_[0];
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# getSerialDevName
|
||||
#
|
||||
# For each device, force to use dev_prefix if it's not an array. Otherwise,
|
||||
# assume it's a list of candidate prefixes. Check them and commit to the
|
||||
# first one that actually exists.
|
||||
#
|
||||
sub getSerialDevName {
|
||||
my $devnum = shift;
|
||||
my $devname = undef;
|
||||
if( defined $devnum ) {
|
||||
if( ref($Opt{dev_prefix}) eq "ARRAY" ) {
|
||||
$devname = $devnum;
|
||||
for my $prefix (@{$Opt{dev_prefix}}) {
|
||||
my $file = $prefix . $devnum;
|
||||
if( -e $file ) { $devname = $file; last; }
|
||||
}
|
||||
} else {
|
||||
$devname = $Opt{dev_prefix} . $devnum;
|
||||
}
|
||||
}
|
||||
return $devname;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Print motelist
|
||||
#
|
||||
sub print_motelist {
|
||||
my @devs = @_;
|
||||
|
||||
# If none were found, quit
|
||||
if( @devs == 0 ) {
|
||||
print "No devices found.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
# Print a header
|
||||
if( !$Opt{compact} ) {
|
||||
if( $Opt{usb} ) {
|
||||
print << "EOF" unless $Opt{compact};
|
||||
Bus Dev USB Path Reference Device Description
|
||||
--- --- ------------------------ ---------- ---------------- -------------------------------------
|
||||
EOF
|
||||
} else {
|
||||
print << "EOF" unless $Opt{compact};
|
||||
Reference Device Description
|
||||
---------- ---------------- ---------------------------------------------
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
# Print the usb information
|
||||
for my $dev (sort { cmp_usbdev($a,$b) } @devs) {
|
||||
my $desc = join( " ", $dev->{InfoManufacturer}||"", $dev->{InfoProduct}||"" ) || " (none)";
|
||||
my @output = ( $dev->{InfoSerial}||" (none)", $dev->{SerialDevName}, $desc );
|
||||
@output = ( $dev->{UsbBusNum}, $dev->{UsbDevNum}, $dev->{UsbPath}, @output ) if $Opt{usb};
|
||||
if( $Opt{compact} ) {
|
||||
print join(",",@output) . "\n";
|
||||
} else {
|
||||
printf( ($Opt{usb}?"%3d %3d %-24s ":"")."%-10s %-16s %s\n", @output );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Cmp Usbdev's
|
||||
#
|
||||
sub cmp_usbdev {
|
||||
my ($a,$b) = @_;
|
||||
if( defined $a->{SerialDevNum} ) {
|
||||
if( defined $b->{SerialDevNum} ) {
|
||||
return $a->{SerialDevNum} <=> $b->{SerialDevNum};
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 1 if defined $b->{SerialDevNum};
|
||||
return ($a->{InfoSerial}||"") cmp ($b->{InfoSerial}||"");
|
||||
}
|
||||
|
||||
#
|
||||
# Read a file in
|
||||
#
|
||||
sub snarf {
|
||||
open my $fh, $_[0] or return undef;
|
||||
my $text = do{local $/;<$fh>};
|
||||
close $fh;
|
||||
$text =~ s/\s+$// if $_[1];
|
||||
return $text;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user