├── .gitignore ├── Makefile ├── README.rst ├── enocean-load.sh ├── fsk-load.sh ├── include ├── linux │ ├── enocean │ ├── lora │ ├── lora.h │ ├── nlfsk.h │ └── nllora.h └── net │ ├── cfgfsk.h │ └── cfglora.h ├── load.sh ├── nltest.c ├── test.c └── txenocean.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.mod.c 3 | *.o 4 | *.ko 5 | modules.order 6 | Module.symvers 7 | .tmp_versions/ 8 | test 9 | nltest 10 | txenocean 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Helpers to build modules out of linux-next based LoRa patch queue 3 | # 4 | 5 | KDIR ?= /lib/modules/`uname -r`/build 6 | 7 | SDIR ?= $$PWD/linux 8 | IDIR = $$PWD/include 9 | 10 | MFLAGS_KCONFIG := CONFIG_LORA=m 11 | MFLAGS_KCONFIG += CONFIG_LORA_DEV=m 12 | MFLAGS_KCONFIG += CONFIG_LORA_MIPOT_32001353=m 13 | MFLAGS_KCONFIG += CONFIG_LORA_MM002=m 14 | MFLAGS_KCONFIG += CONFIG_LORA_RAK811=m 15 | MFLAGS_KCONFIG += CONFIG_LORA_RF1276TS=m 16 | MFLAGS_KCONFIG += CONFIG_LORA_RN2483=m 17 | MFLAGS_KCONFIG += CONFIG_LORA_SX125X_CORE=m 18 | MFLAGS_KCONFIG += CONFIG_LORA_SX127X=m 19 | MFLAGS_KCONFIG += CONFIG_LORA_SX128X=m 20 | MFLAGS_KCONFIG += CONFIG_LORA_SX128X_SPI=y 21 | MFLAGS_KCONFIG += CONFIG_LORA_SX130X=m 22 | MFLAGS_KCONFIG += CONFIG_LORA_TING01M=m 23 | MFLAGS_KCONFIG += CONFIG_LORA_USI=m 24 | MFLAGS_KCONFIG += CONFIG_LORA_WIMOD=m 25 | 26 | MFLAGS_KCONFIG += CONFIG_FSK=m 27 | MFLAGS_KCONFIG += CONFIG_FSK_CC1120=m 28 | MFLAGS_KCONFIG += CONFIG_FSK_NRF24L01P=m 29 | MFLAGS_KCONFIG += CONFIG_FSK_MRF89XA=m 30 | MFLAGS_KCONFIG += CONFIG_FSK_S2LP=m 31 | MFLAGS_KCONFIG += CONFIG_FSK_SI443X=m 32 | 33 | all: test 34 | # $(MAKE) -C $(KDIR) M=$$PWD 35 | $(MAKE) -C $(KDIR) M=$(SDIR)/net/fsk \ 36 | $(MFLAGS_KCONFIG) \ 37 | CFLAGS_MODULE=-I$(IDIR) 38 | $(MAKE) -C $(KDIR) M=$(SDIR)/net/lora \ 39 | $(MFLAGS_KCONFIG) \ 40 | CFLAGS_MODULE=-I$(IDIR) 41 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/lora \ 42 | $(MFLAGS_KCONFIG) \ 43 | KBUILD_EXTRA_SYMBOLS="$(SDIR)/net/lora/Module.symvers $(SDIR)/net/fsk/Module.symvers" \ 44 | CFLAGS_MODULE="-I$(IDIR) -DCONFIG_FSK -DCONFIG_LORA_SX125X_CON -DCONFIG_LORA_SX128X_SPI" 45 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/lorawan \ 46 | $(MFLAGS_KCONFIG) \ 47 | KBUILD_EXTRA_SYMBOLS=$(SDIR)/net/lora/Module.symvers \ 48 | CFLAGS_MODULE=-I$(IDIR) 49 | 50 | fsk: 51 | $(MAKE) -C $(KDIR) M=$(SDIR)/net/fsk \ 52 | $(MFLAGS_KCONFIG) \ 53 | CFLAGS_MODULE=-I$(IDIR) 54 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/fsk \ 55 | $(MFLAGS_KCONFIG) \ 56 | KBUILD_EXTRA_SYMBOLS=$(SDIR)/net/fsk/Module.symvers \ 57 | CFLAGS_MODULE=-I$(IDIR) 58 | 59 | enocean: 60 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/enocean \ 61 | CFLAGS_MODULE=-I$(IDIR) 62 | 63 | usb: 64 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/usb/class cdc-acm.ko 65 | 66 | modules_install: 67 | for m in $(SDIR)/net/lora $(SDIR)/drivers/net/lora; do \ 68 | $(MAKE) -C $(KDIR) M=$$m $(MFLAGS_KCONFIG) modules_install; \ 69 | done 70 | 71 | clean: 72 | $(MAKE) -C $(KDIR) M=$(SDIR)/net/lora $(MFLAGS_KCONFIG) clean 73 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/lora $(MFLAGS_KCONFIG) clean 74 | @rm -f test nltest 75 | 76 | clean-fsk: 77 | $(MAKE) -C $(KDIR) M=$(SDIR)/net/fsk $(MFLAGS_KCONFIG) clean 78 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/fsk $(MFLAGS_KCONFIG) clean 79 | 80 | clean-enocean: 81 | $(MAKE) -C $(KDIR) M=$(SDIR)/drivers/net/enocean clean 82 | 83 | test: test.c 84 | $(CC) -o test test.c 85 | 86 | txenocean: txenocean.c 87 | $(CC) -o txenocean txenocean.c 88 | 89 | nltest: nltest.c 90 | $(CC) $(shell pkg-config --cflags --libs libnl-genl-3.0) -o nltest nltest.c 91 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | LoRa modules for Linux 2 | ====================== 3 | 4 | This repository used to contain source code of Linux kernel modules 5 | for a PF_LORA socket implementation and multiple chipset drivers. 6 | 7 | Today it contains a Makefile for building those modules from an external 8 | Linux repository. 9 | 10 | It also contains a userspace example program for sending a packet. 11 | 12 | Usage 13 | ----- 14 | 15 | To build the kernel modules for a distro kernel (e.g., openSUSE Tumbleweed): 16 | 17 | :: 18 | 19 | $ git clone https://github.com/afaerber/lora-modules.git 20 | $ cd lora-modules 21 | $ git clone https://git.kernel.org/pub/scm/linux/kernel/git/afaerber/linux-lora.git -b lora-next 22 | 23 | Review the lora-modules.git file include/linux/lora.h, 24 | which reuses some existing number for AF_LORA lower than AF_MAX, 25 | as well as two free-at-the-time ARPHRD and ETH_P numbers. 26 | You may need to change these numbers to avoid conflicts. 27 | 28 | :: 29 | 30 | $ make 31 | 32 | Before you attempt to load any of the modules, 33 | always review what they are currently doing! 34 | They might have a frequency hardcoded not suited for your region, 35 | or might do other unexpected things for testing purposes. 36 | 37 | To go ahead and load the modules locally, tainting your kernel: 38 | 39 | :: 40 | 41 | # ./load.sh 42 | 43 | That will insmod the set of drivers, but the chipset drivers won't probe 44 | unless you're using a Device Tree Overlay for your board and chipset. 45 | 46 | Device Tree Overlays 47 | -------------------- 48 | 49 | Examples of DT Overlays can be found here: 50 | https://github.com/afaerber/dt-overlays 51 | 52 | To apply a DT Overlay on the Raspberry Pi, use ``dtoverlay=foo`` in 53 | config.txt (extraconfig.txt on openSUSE and SUSE Linux Enterprise Server 15). 54 | 55 | To apply a DT Overlay on boards using U-Boot, use the ``fdt apply`` command. 56 | 57 | On other boards you may have to resort to replacing the whole Device Tree. 58 | 59 | Browse the openSUSE HCL Wiki for specific expansion board instructions. 60 | 61 | Have a lot of fun! 62 | -------------------------------------------------------------------------------- /enocean-load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rmmod enocean-esp 4 | rmmod enocean-dev 5 | 6 | set -e 7 | 8 | modprobe crc8 9 | 10 | BDIR=linux 11 | 12 | insmod ${BDIR}/drivers/net/enocean/enocean-dev.ko dyndbg 13 | insmod ${BDIR}/drivers/net/enocean/enocean-esp.ko dyndbg 14 | -------------------------------------------------------------------------------- /fsk-load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rmmod fsk-nrf24l01p 4 | rmmod fsk-si443x 5 | rmmod cfgfsk 6 | 7 | set -e 8 | 9 | BDIR=linux 10 | 11 | insmod ${BDIR}/net/fsk/cfgfsk.ko dyndbg 12 | 13 | insmod ${BDIR}/drivers/net/fsk/fsk-nrf24l01p.ko dyndbg 14 | insmod ${BDIR}/drivers/net/fsk/fsk-si443x.ko dyndbg 15 | -------------------------------------------------------------------------------- /include/linux/enocean: -------------------------------------------------------------------------------- 1 | ../../linux/include/linux/enocean -------------------------------------------------------------------------------- /include/linux/lora: -------------------------------------------------------------------------------- 1 | ../../linux/include/linux/lora -------------------------------------------------------------------------------- /include/linux/lora.h: -------------------------------------------------------------------------------- 1 | #include "../../linux/include/uapi/linux/lora.h" 2 | 3 | #define AF_LORA 28 4 | #define PF_LORA AF_LORA 5 | 6 | #define ARPHRD_LORA 827 7 | 8 | #define ETH_P_LORA 0x00FA 9 | #define ETH_P_LORAWAN 0x80FB 10 | #define ETH_P_OOK 0x80FC 11 | #define ETH_P_FSK 0x80FD 12 | #define ETH_P_FLRC 0x80FE 13 | -------------------------------------------------------------------------------- /include/linux/nlfsk.h: -------------------------------------------------------------------------------- 1 | ../../linux/include/uapi/linux/nlfsk.h -------------------------------------------------------------------------------- /include/linux/nllora.h: -------------------------------------------------------------------------------- 1 | ../../linux/include/uapi/linux/nllora.h -------------------------------------------------------------------------------- /include/net/cfgfsk.h: -------------------------------------------------------------------------------- 1 | ../../linux/include/net/cfgfsk.h -------------------------------------------------------------------------------- /include/net/cfglora.h: -------------------------------------------------------------------------------- 1 | ../../linux/include/net/cfglora.h -------------------------------------------------------------------------------- /load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rmmod lora-sx125x 4 | rmmod lora-sx1301 5 | rmmod lora-sx130x-picogw 6 | rmmod lora-sx130x 7 | rmmod lora-sx1257 8 | rmmod lora-picogw 9 | rmmod lora-sx128x 10 | rmmod lora-sx1276 11 | rmmod lora-sx127x 12 | rmmod lora-rn2483 13 | rmmod lora-wimod 14 | rmmod lora-usi 15 | rmmod lora-rak811 16 | rmmod lora-ting01m 17 | rmmod lora-mm002 18 | rmmod lora-rf1276ts 19 | rmmod lora-mipot32001353 20 | rmmod lora-dev 21 | 22 | rmmod nllora 23 | rmmod cfglora 24 | rmmod lora 25 | 26 | rmmod cfgfsk 27 | 28 | set -e 29 | 30 | BDIR=linux 31 | 32 | insmod ${BDIR}/net/fsk/cfgfsk.ko 33 | 34 | insmod ${BDIR}/net/lora/lora.ko 35 | insmod ${BDIR}/net/lora/cfglora.ko 36 | 37 | insmod ${BDIR}/drivers/net/lora/lora-dev.ko 38 | insmod ${BDIR}/drivers/net/lora/lora-rn2483.ko dyndbg 39 | insmod ${BDIR}/drivers/net/lora/lora-wimod.ko dyndbg 40 | insmod ${BDIR}/drivers/net/lora/lora-usi.ko dyndbg 41 | insmod ${BDIR}/drivers/net/lora/lora-rak811.ko dyndbg 42 | insmod ${BDIR}/drivers/net/lora/lora-ting01m.ko dyndbg 43 | insmod ${BDIR}/drivers/net/lora/lora-mm002.ko dyndbg 44 | insmod ${BDIR}/drivers/net/lora/lora-rf1276ts.ko dyndbg 45 | insmod ${BDIR}/drivers/net/lora/lora-mipot32001353.ko dyndbg 46 | insmod ${BDIR}/drivers/net/lora/lora-sx127x.ko dyndbg 47 | insmod ${BDIR}/drivers/net/lora/lora-sx128x.ko dyndbg 48 | insmod ${BDIR}/drivers/net/lora/lora-sx130x.ko dyndbg 49 | insmod ${BDIR}/drivers/net/lora/lora-sx125x.ko dyndbg 50 | insmod ${BDIR}/drivers/net/lora/lora-sx130x-picogw.ko dyndbg 51 | insmod ${BDIR}/drivers/net/lora/lora-picogw.ko dyndbg 52 | -------------------------------------------------------------------------------- /nltest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "include/linux/lora.h" 15 | #include "include/linux/nllora.h" 16 | #include "include/linux/nlfsk.h" 17 | 18 | static struct nla_policy my_lora_policy[NLLORA_ATTR_MAX + 1] = { 19 | [NLLORA_ATTR_IFINDEX] = { .type = NLA_U32 }, 20 | [NLLORA_ATTR_FREQ] = { .type = NLA_U32 }, 21 | [NLLORA_ATTR_TX_POWER] = { .type = NLA_S32 }, 22 | }; 23 | 24 | static struct nla_policy my_fsk_policy[NLFSK_ATTR_MAX + 1] = { 25 | [NLFSK_ATTR_IFINDEX] = { .type = NLA_U32 }, 26 | [NLFSK_ATTR_FREQ] = { .type = NLA_U32 }, 27 | [NLFSK_ATTR_TX_POWER] = { .type = NLA_S32 }, 28 | }; 29 | 30 | static int seq_check(struct nl_msg *msg, void *arg) 31 | { 32 | return NL_OK; 33 | } 34 | 35 | static int nllora_get_freq_val(struct nl_msg *msg, void *arg) 36 | { 37 | struct nlattr *attrs[NLLORA_ATTR_MAX + 1]; 38 | uint32_t *freq = arg; 39 | 40 | genlmsg_parse(nlmsg_hdr(msg), 0, attrs, NLLORA_ATTR_MAX, my_lora_policy); 41 | 42 | if (!attrs[NLLORA_ATTR_FREQ]) 43 | return NL_SKIP; 44 | 45 | *freq = nla_get_u32(attrs[NLLORA_ATTR_FREQ]); 46 | 47 | return NL_OK; 48 | } 49 | 50 | static int nllora_get_freq(struct nl_sock *sk, int family_id, 51 | int ifindex, uint32_t *val) 52 | { 53 | struct nl_msg *msg; 54 | struct nl_cb *cb; 55 | void *ptr; 56 | int ret; 57 | 58 | msg = nlmsg_alloc(); 59 | if (msg == NULL) { 60 | fprintf(stderr, "nlmsg_alloc\n"); 61 | return -ENOMEM; 62 | } 63 | 64 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLLORA_CMD_GET_FREQ, 0); 65 | if (ptr == NULL) { 66 | fprintf(stderr, "genlmsg_put\n"); 67 | nlmsg_free(msg); 68 | return -ENOMEM; 69 | } 70 | 71 | ret = nla_put_u32(msg, NLLORA_ATTR_IFINDEX, ifindex); 72 | if (ret < 0) { 73 | fprintf(stderr, "nla_put_u32\n"); 74 | nlmsg_free(msg); 75 | return ret; 76 | } 77 | 78 | ret = nl_send_auto(sk, msg); 79 | if (ret < 0) { 80 | fprintf(stderr, "nl_send_auto\n"); 81 | nlmsg_free(msg); 82 | return ret; 83 | } 84 | 85 | nlmsg_free(msg); 86 | 87 | cb = nl_cb_alloc(NL_CB_DEFAULT); 88 | if (cb == NULL) { 89 | fprintf(stderr, "nl_cb_alloc\n"); 90 | return -ENOMEM; 91 | } 92 | 93 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 94 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nllora_get_freq_val, val); 95 | 96 | ret = nl_recvmsgs(sk, cb); 97 | 98 | nl_cb_put(cb); 99 | 100 | return ret; 101 | } 102 | 103 | static int nllora_set_freq(struct nl_sock *sk, int family_id, 104 | int ifindex, uint32_t val) 105 | { 106 | struct nl_msg *msg; 107 | struct nl_cb *cb; 108 | void *ptr; 109 | int ret; 110 | 111 | msg = nlmsg_alloc(); 112 | if (msg == NULL) { 113 | fprintf(stderr, "nlmsg_alloc\n"); 114 | return -ENOMEM; 115 | } 116 | 117 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLLORA_CMD_SET_FREQ, 0); 118 | if (ptr == NULL) { 119 | fprintf(stderr, "genlmsg_put\n"); 120 | nlmsg_free(msg); 121 | return -ENOMEM; 122 | } 123 | 124 | ret = nla_put_u32(msg, NLLORA_ATTR_IFINDEX, ifindex); 125 | if (ret < 0) { 126 | fprintf(stderr, "nla_put_u32\n"); 127 | nlmsg_free(msg); 128 | return ret; 129 | } 130 | 131 | ret = nla_put_u32(msg, NLLORA_ATTR_FREQ, val); 132 | if (ret < 0) { 133 | fprintf(stderr, "nla_put_u32 2\n"); 134 | nlmsg_free(msg); 135 | return ret; 136 | } 137 | 138 | ret = nl_send_auto(sk, msg); 139 | if (ret < 0) { 140 | fprintf(stderr, "nl_send_auto\n"); 141 | nlmsg_free(msg); 142 | return ret; 143 | } 144 | 145 | nlmsg_free(msg); 146 | 147 | cb = nl_cb_alloc(NL_CB_DEFAULT); 148 | if (cb == NULL) { 149 | fprintf(stderr, "nl_cb_alloc\n"); 150 | return -ENOMEM; 151 | } 152 | 153 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 154 | 155 | ret = nl_recvmsgs(sk, cb); 156 | 157 | nl_cb_put(cb); 158 | 159 | return ret; 160 | } 161 | 162 | static int nllora_get_tx_power_val(struct nl_msg *msg, void *arg) 163 | { 164 | struct nlattr *attrs[NLLORA_ATTR_MAX + 1]; 165 | uint32_t *freq = arg; 166 | 167 | genlmsg_parse(nlmsg_hdr(msg), 0, attrs, NLLORA_ATTR_MAX, my_lora_policy); 168 | 169 | if (!attrs[NLLORA_ATTR_TX_POWER]) 170 | return NL_SKIP; 171 | 172 | *freq = nla_get_u32(attrs[NLLORA_ATTR_TX_POWER]); 173 | 174 | return NL_OK; 175 | } 176 | 177 | static int nllora_get_tx_power(struct nl_sock *sk, int family_id, 178 | int ifindex, int32_t *val) 179 | { 180 | struct nl_msg *msg; 181 | struct nl_cb *cb; 182 | void *ptr; 183 | int ret; 184 | 185 | msg = nlmsg_alloc(); 186 | if (msg == NULL) { 187 | fprintf(stderr, "nlmsg_alloc\n"); 188 | return -ENOMEM; 189 | } 190 | 191 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLLORA_CMD_GET_TX_POWER, 0); 192 | if (ptr == NULL) { 193 | fprintf(stderr, "genlmsg_put\n"); 194 | nlmsg_free(msg); 195 | return -ENOMEM; 196 | } 197 | 198 | ret = nla_put_u32(msg, NLLORA_ATTR_IFINDEX, ifindex); 199 | if (ret < 0) { 200 | fprintf(stderr, "nla_put_u32\n"); 201 | nlmsg_free(msg); 202 | return ret; 203 | } 204 | 205 | ret = nl_send_auto(sk, msg); 206 | if (ret < 0) { 207 | fprintf(stderr, "nl_send_auto\n"); 208 | nlmsg_free(msg); 209 | return ret; 210 | } 211 | 212 | nlmsg_free(msg); 213 | 214 | cb = nl_cb_alloc(NL_CB_DEFAULT); 215 | if (cb == NULL) { 216 | fprintf(stderr, "nl_cb_alloc\n"); 217 | return -ENOMEM; 218 | } 219 | 220 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 221 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nllora_get_tx_power_val, val); 222 | 223 | ret = nl_recvmsgs(sk, cb); 224 | 225 | nl_cb_put(cb); 226 | 227 | return ret; 228 | } 229 | 230 | static int nllora_set_tx_power(struct nl_sock *sk, int family_id, 231 | int ifindex, int32_t val) 232 | { 233 | struct nl_msg *msg; 234 | struct nl_cb *cb; 235 | void *ptr; 236 | int ret; 237 | 238 | msg = nlmsg_alloc(); 239 | if (msg == NULL) { 240 | fprintf(stderr, "nlmsg_alloc\n"); 241 | return -ENOMEM; 242 | } 243 | 244 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLLORA_CMD_SET_TX_POWER, 0); 245 | if (ptr == NULL) { 246 | fprintf(stderr, "genlmsg_put\n"); 247 | nlmsg_free(msg); 248 | return -ENOMEM; 249 | } 250 | 251 | ret = nla_put_u32(msg, NLLORA_ATTR_IFINDEX, ifindex); 252 | if (ret < 0) { 253 | fprintf(stderr, "nla_put_u32\n"); 254 | nlmsg_free(msg); 255 | return ret; 256 | } 257 | 258 | ret = nla_put_s32(msg, NLLORA_ATTR_TX_POWER, val); 259 | if (ret < 0) { 260 | fprintf(stderr, "nla_put_s32\n"); 261 | nlmsg_free(msg); 262 | return ret; 263 | } 264 | 265 | ret = nl_send_auto(sk, msg); 266 | if (ret < 0) { 267 | fprintf(stderr, "nl_send_auto\n"); 268 | nlmsg_free(msg); 269 | return ret; 270 | } 271 | 272 | nlmsg_free(msg); 273 | 274 | cb = nl_cb_alloc(NL_CB_DEFAULT); 275 | if (cb == NULL) { 276 | fprintf(stderr, "nl_cb_alloc\n"); 277 | return -ENOMEM; 278 | } 279 | 280 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 281 | 282 | ret = nl_recvmsgs(sk, cb); 283 | 284 | nl_cb_put(cb); 285 | 286 | return ret; 287 | } 288 | 289 | static int nlfsk_get_freq_val(struct nl_msg *msg, void *arg) 290 | { 291 | struct nlattr *attrs[NLFSK_ATTR_MAX + 1]; 292 | uint32_t *freq = arg; 293 | 294 | genlmsg_parse(nlmsg_hdr(msg), 0, attrs, NLFSK_ATTR_MAX, my_fsk_policy); 295 | 296 | if (!attrs[NLFSK_ATTR_FREQ]) 297 | return NL_SKIP; 298 | 299 | *freq = nla_get_u32(attrs[NLFSK_ATTR_FREQ]); 300 | 301 | return NL_OK; 302 | } 303 | 304 | static int nlfsk_get_freq(struct nl_sock *sk, int family_id, 305 | int ifindex, uint32_t *val) 306 | { 307 | struct nl_msg *msg; 308 | struct nl_cb *cb; 309 | void *ptr; 310 | int ret; 311 | 312 | msg = nlmsg_alloc(); 313 | if (msg == NULL) { 314 | fprintf(stderr, "nlmsg_alloc\n"); 315 | return -ENOMEM; 316 | } 317 | 318 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_GET_FREQ, 0); 319 | if (ptr == NULL) { 320 | fprintf(stderr, "genlmsg_put\n"); 321 | nlmsg_free(msg); 322 | return -ENOMEM; 323 | } 324 | 325 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 326 | if (ret < 0) { 327 | fprintf(stderr, "nla_put_u32\n"); 328 | nlmsg_free(msg); 329 | return ret; 330 | } 331 | 332 | ret = nl_send_auto(sk, msg); 333 | if (ret < 0) { 334 | fprintf(stderr, "nl_send_auto\n"); 335 | nlmsg_free(msg); 336 | return ret; 337 | } 338 | 339 | nlmsg_free(msg); 340 | 341 | cb = nl_cb_alloc(NL_CB_DEFAULT); 342 | if (cb == NULL) { 343 | fprintf(stderr, "nl_cb_alloc\n"); 344 | return -ENOMEM; 345 | } 346 | 347 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 348 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nlfsk_get_freq_val, val); 349 | 350 | ret = nl_recvmsgs(sk, cb); 351 | 352 | nl_cb_put(cb); 353 | 354 | return ret; 355 | } 356 | 357 | static int nlfsk_set_freq(struct nl_sock *sk, int family_id, 358 | int ifindex, uint32_t val) 359 | { 360 | struct nl_msg *msg; 361 | struct nl_cb *cb; 362 | void *ptr; 363 | int ret; 364 | 365 | msg = nlmsg_alloc(); 366 | if (msg == NULL) { 367 | fprintf(stderr, "nlmsg_alloc\n"); 368 | return -ENOMEM; 369 | } 370 | 371 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_SET_FREQ, 0); 372 | if (ptr == NULL) { 373 | fprintf(stderr, "genlmsg_put\n"); 374 | nlmsg_free(msg); 375 | return -ENOMEM; 376 | } 377 | 378 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 379 | if (ret < 0) { 380 | fprintf(stderr, "nla_put_u32\n"); 381 | nlmsg_free(msg); 382 | return ret; 383 | } 384 | 385 | ret = nla_put_u32(msg, NLFSK_ATTR_FREQ, val); 386 | if (ret < 0) { 387 | fprintf(stderr, "nla_put_u32 2\n"); 388 | nlmsg_free(msg); 389 | return ret; 390 | } 391 | 392 | ret = nl_send_auto(sk, msg); 393 | if (ret < 0) { 394 | fprintf(stderr, "nl_send_auto\n"); 395 | nlmsg_free(msg); 396 | return ret; 397 | } 398 | 399 | nlmsg_free(msg); 400 | 401 | cb = nl_cb_alloc(NL_CB_DEFAULT); 402 | if (cb == NULL) { 403 | fprintf(stderr, "nl_cb_alloc\n"); 404 | return -ENOMEM; 405 | } 406 | 407 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 408 | 409 | ret = nl_recvmsgs(sk, cb); 410 | 411 | nl_cb_put(cb); 412 | 413 | return ret; 414 | } 415 | 416 | static int nlfsk_get_freq_dev(struct nl_sock *sk, int family_id, 417 | int ifindex, uint32_t *val) 418 | { 419 | struct nl_msg *msg; 420 | struct nl_cb *cb; 421 | void *ptr; 422 | int ret; 423 | 424 | msg = nlmsg_alloc(); 425 | if (msg == NULL) { 426 | fprintf(stderr, "nlmsg_alloc\n"); 427 | return -ENOMEM; 428 | } 429 | 430 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_GET_FREQ_DEV, 0); 431 | if (ptr == NULL) { 432 | fprintf(stderr, "genlmsg_put\n"); 433 | nlmsg_free(msg); 434 | return -ENOMEM; 435 | } 436 | 437 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 438 | if (ret < 0) { 439 | fprintf(stderr, "nla_put_u32\n"); 440 | nlmsg_free(msg); 441 | return ret; 442 | } 443 | 444 | ret = nl_send_auto(sk, msg); 445 | if (ret < 0) { 446 | fprintf(stderr, "nl_send_auto\n"); 447 | nlmsg_free(msg); 448 | return ret; 449 | } 450 | 451 | nlmsg_free(msg); 452 | 453 | cb = nl_cb_alloc(NL_CB_DEFAULT); 454 | if (cb == NULL) { 455 | fprintf(stderr, "nl_cb_alloc\n"); 456 | return -ENOMEM; 457 | } 458 | 459 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 460 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nlfsk_get_freq_val, val); 461 | 462 | ret = nl_recvmsgs(sk, cb); 463 | 464 | nl_cb_put(cb); 465 | 466 | return ret; 467 | } 468 | 469 | static int nlfsk_set_freq_dev(struct nl_sock *sk, int family_id, 470 | int ifindex, uint32_t val) 471 | { 472 | struct nl_msg *msg; 473 | struct nl_cb *cb; 474 | void *ptr; 475 | int ret; 476 | 477 | msg = nlmsg_alloc(); 478 | if (msg == NULL) { 479 | fprintf(stderr, "nlmsg_alloc\n"); 480 | return -ENOMEM; 481 | } 482 | 483 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_SET_FREQ_DEV, 0); 484 | if (ptr == NULL) { 485 | fprintf(stderr, "genlmsg_put\n"); 486 | nlmsg_free(msg); 487 | return -ENOMEM; 488 | } 489 | 490 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 491 | if (ret < 0) { 492 | fprintf(stderr, "nla_put_u32\n"); 493 | nlmsg_free(msg); 494 | return ret; 495 | } 496 | 497 | ret = nla_put_u32(msg, NLFSK_ATTR_FREQ, val); 498 | if (ret < 0) { 499 | fprintf(stderr, "nla_put_u32 2\n"); 500 | nlmsg_free(msg); 501 | return ret; 502 | } 503 | 504 | ret = nl_send_auto(sk, msg); 505 | if (ret < 0) { 506 | fprintf(stderr, "nl_send_auto\n"); 507 | nlmsg_free(msg); 508 | return ret; 509 | } 510 | 511 | nlmsg_free(msg); 512 | 513 | cb = nl_cb_alloc(NL_CB_DEFAULT); 514 | if (cb == NULL) { 515 | fprintf(stderr, "nl_cb_alloc\n"); 516 | return -ENOMEM; 517 | } 518 | 519 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 520 | 521 | ret = nl_recvmsgs(sk, cb); 522 | 523 | nl_cb_put(cb); 524 | 525 | return ret; 526 | } 527 | 528 | static int nlfsk_get_tx_power_val(struct nl_msg *msg, void *arg) 529 | { 530 | struct nlattr *attrs[NLFSK_ATTR_MAX + 1]; 531 | uint32_t *tx_power = arg; 532 | 533 | genlmsg_parse(nlmsg_hdr(msg), 0, attrs, NLFSK_ATTR_MAX, my_fsk_policy); 534 | 535 | if (!attrs[NLFSK_ATTR_TX_POWER]) 536 | return NL_SKIP; 537 | 538 | *tx_power = nla_get_u32(attrs[NLFSK_ATTR_TX_POWER]); 539 | 540 | return NL_OK; 541 | } 542 | 543 | static int nlfsk_get_tx_power(struct nl_sock *sk, int family_id, 544 | int ifindex, int32_t *val) 545 | { 546 | struct nl_msg *msg; 547 | struct nl_cb *cb; 548 | void *ptr; 549 | int ret; 550 | 551 | msg = nlmsg_alloc(); 552 | if (msg == NULL) { 553 | fprintf(stderr, "nlmsg_alloc\n"); 554 | return -ENOMEM; 555 | } 556 | 557 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_GET_TX_POWER, 0); 558 | if (ptr == NULL) { 559 | fprintf(stderr, "genlmsg_put\n"); 560 | nlmsg_free(msg); 561 | return -ENOMEM; 562 | } 563 | 564 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 565 | if (ret < 0) { 566 | fprintf(stderr, "nla_put_u32\n"); 567 | nlmsg_free(msg); 568 | return ret; 569 | } 570 | 571 | ret = nl_send_auto(sk, msg); 572 | if (ret < 0) { 573 | fprintf(stderr, "nl_send_auto\n"); 574 | nlmsg_free(msg); 575 | return ret; 576 | } 577 | 578 | nlmsg_free(msg); 579 | 580 | cb = nl_cb_alloc(NL_CB_DEFAULT); 581 | if (cb == NULL) { 582 | fprintf(stderr, "nl_cb_alloc\n"); 583 | return -ENOMEM; 584 | } 585 | 586 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 587 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nlfsk_get_tx_power_val, val); 588 | 589 | ret = nl_recvmsgs(sk, cb); 590 | 591 | nl_cb_put(cb); 592 | 593 | return ret; 594 | } 595 | 596 | static int nlfsk_set_tx_power(struct nl_sock *sk, int family_id, 597 | int ifindex, int32_t val) 598 | { 599 | struct nl_msg *msg; 600 | struct nl_cb *cb; 601 | void *ptr; 602 | int ret; 603 | 604 | msg = nlmsg_alloc(); 605 | if (msg == NULL) { 606 | fprintf(stderr, "nlmsg_alloc\n"); 607 | return -ENOMEM; 608 | } 609 | 610 | ptr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, NLM_F_REQUEST, NLFSK_CMD_SET_TX_POWER, 0); 611 | if (ptr == NULL) { 612 | fprintf(stderr, "genlmsg_put\n"); 613 | nlmsg_free(msg); 614 | return -ENOMEM; 615 | } 616 | 617 | ret = nla_put_u32(msg, NLFSK_ATTR_IFINDEX, ifindex); 618 | if (ret < 0) { 619 | fprintf(stderr, "nla_put_u32\n"); 620 | nlmsg_free(msg); 621 | return ret; 622 | } 623 | 624 | ret = nla_put_s32(msg, NLFSK_ATTR_TX_POWER, val); 625 | if (ret < 0) { 626 | fprintf(stderr, "nla_put_s32\n"); 627 | nlmsg_free(msg); 628 | return ret; 629 | } 630 | 631 | ret = nl_send_auto(sk, msg); 632 | if (ret < 0) { 633 | fprintf(stderr, "nl_send_auto\n"); 634 | nlmsg_free(msg); 635 | return ret; 636 | } 637 | 638 | nlmsg_free(msg); 639 | 640 | cb = nl_cb_alloc(NL_CB_DEFAULT); 641 | if (cb == NULL) { 642 | fprintf(stderr, "nl_cb_alloc\n"); 643 | return -ENOMEM; 644 | } 645 | 646 | nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check, NULL); 647 | 648 | ret = nl_recvmsgs(sk, cb); 649 | 650 | nl_cb_put(cb); 651 | 652 | return ret; 653 | } 654 | 655 | static int get_ifindex(const char *ifname, const char *mode, int *ifindex) 656 | { 657 | struct ifreq ifr; 658 | int skt, ret; 659 | 660 | if (strcmp(mode, "lora") == 0) { 661 | //skt = socket(PF_LORA, SOCK_DGRAM, 1); 662 | skt = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_LORA)); 663 | } else if (strcmp(mode, "fsk") == 0) { 664 | skt = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FSK)); 665 | } else return -EINVAL; 666 | if (skt == -1) { 667 | int err = errno; 668 | fprintf(stderr, "socket failed: %s (%d)\n", strerror(err), err); 669 | return -err; 670 | } 671 | 672 | strcpy(ifr.ifr_name, ifname); 673 | ret = ioctl(skt, SIOCGIFINDEX, &ifr); 674 | if (ret == -1) { 675 | int err = errno; 676 | fprintf(stderr, "ioctl failed: %s (%d)\n", strerror(err), err); 677 | close(skt); 678 | return -err; 679 | } 680 | close(skt); 681 | 682 | *ifindex = ifr.ifr_ifindex; 683 | return 0; 684 | } 685 | 686 | static int handle_lora(struct nl_sock *sk, int family_id, int ifindex, const char *cmd, 687 | int argc, char **args) 688 | { 689 | uint32_t freq; 690 | int32_t tx_power; 691 | char *endptr; 692 | int ret; 693 | 694 | if (strcmp(cmd, "freq") == 0) { 695 | if (argc == 0) { 696 | ret = nllora_get_freq(sk, family_id, ifindex, &freq); 697 | if (ret) { 698 | fprintf(stderr, "nllora_get_freq\n"); 699 | return 1; 700 | } 701 | printf("frequency: %u\n", freq); 702 | } else if (argc == 1) { 703 | freq = strtoul(args[0], &endptr, 0); 704 | if (endptr == args[0]) { 705 | fprintf(stderr, "invalid argument\n"); 706 | return 1; 707 | } 708 | ret = nllora_set_freq(sk, family_id, ifindex, freq); 709 | if (ret) { 710 | fprintf(stderr, "nllora_set_freq\n"); 711 | return 1; 712 | } 713 | } else return -EINVAL; 714 | } else if (strcmp(cmd, "tx_power") == 0) { 715 | if (argc == 0) { 716 | ret = nllora_get_tx_power(sk, family_id, ifindex, &tx_power); 717 | if (ret) { 718 | fprintf(stderr, "nllora_get_tx_power\n"); 719 | return 1; 720 | } 721 | printf("tx power: %d\n", tx_power); 722 | } else if (argc == 1) { 723 | tx_power = strtol(args[0], &endptr, 0); 724 | if (endptr == args[0]) { 725 | fprintf(stderr, "invalid argument\n"); 726 | return 1; 727 | } 728 | ret = nllora_set_tx_power(sk, family_id, ifindex, tx_power); 729 | if (ret) { 730 | fprintf(stderr, "nllora_set_tx_power\n"); 731 | return 1; 732 | } 733 | } else return -EINVAL; 734 | } else return -EINVAL; 735 | return 0; 736 | } 737 | 738 | static int handle_fsk(struct nl_sock *sk, int family_id, int ifindex, const char *cmd, 739 | int argc, char **args) 740 | { 741 | uint32_t freq; 742 | int32_t tx_power; 743 | char *endptr; 744 | int ret; 745 | 746 | if (strcmp(cmd, "freq") == 0) { 747 | if (argc == 0) { 748 | ret = nlfsk_get_freq(sk, family_id, ifindex, &freq); 749 | if (ret) { 750 | fprintf(stderr, "nlfsk_get_freq\n"); 751 | return 1; 752 | } 753 | printf("frequency: %u\n", freq); 754 | } else if (argc == 1) { 755 | freq = strtoul(args[0], &endptr, 0); 756 | if (endptr == args[0]) { 757 | fprintf(stderr, "invalid argument\n"); 758 | return 1; 759 | } 760 | ret = nlfsk_set_freq(sk, family_id, ifindex, freq); 761 | if (ret) { 762 | fprintf(stderr, "nlfsk_set_freq\n"); 763 | return 1; 764 | } 765 | } else return -EINVAL; 766 | } else if (strcmp(cmd, "freq_dev") == 0) { 767 | if (argc == 0) { 768 | ret = nlfsk_get_freq_dev(sk, family_id, ifindex, &freq); 769 | if (ret) { 770 | fprintf(stderr, "nlfsk_get_freq_dev\n"); 771 | return 1; 772 | } 773 | printf("frequency deviation: %u\n", freq); 774 | } else if (argc == 1) { 775 | freq = strtoul(args[0], &endptr, 0); 776 | if (endptr == args[0]) { 777 | fprintf(stderr, "invalid argument\n"); 778 | return 1; 779 | } 780 | ret = nlfsk_set_freq_dev(sk, family_id, ifindex, freq); 781 | if (ret) { 782 | fprintf(stderr, "nlfsk_set_freq_dev\n"); 783 | return 1; 784 | } 785 | } else return -EINVAL; 786 | } else if (strcmp(cmd, "tx_power") == 0) { 787 | if (argc == 0) { 788 | ret = nlfsk_get_tx_power(sk, family_id, ifindex, &tx_power); 789 | if (ret) { 790 | fprintf(stderr, "nlfsk_get_tx_power\n"); 791 | return 1; 792 | } 793 | printf("tx power: %d\n", tx_power); 794 | } else if (argc == 1) { 795 | tx_power = strtol(args[0], &endptr, 0); 796 | if (endptr == args[0]) { 797 | fprintf(stderr, "invalid argument\n"); 798 | return 1; 799 | } 800 | ret = nlfsk_set_tx_power(sk, family_id, ifindex, tx_power); 801 | if (ret) { 802 | fprintf(stderr, "nlfsk_set_tx_power\n"); 803 | return 1; 804 | } 805 | } else return -EINVAL; 806 | } else return -EINVAL; 807 | return 0; 808 | } 809 | 810 | static int usage(const char *argv0) 811 | { 812 | fprintf(stderr, "Usage: %s lora0 lora|fsk op\n", argv0); 813 | return 2; 814 | } 815 | 816 | int main(int argc, char **argv) 817 | { 818 | struct nl_sock *sk; 819 | const char *name; 820 | int ifindex, family_id, ret; 821 | 822 | if (argc < 1 + 3) { 823 | return usage(argv[0]); 824 | } 825 | 826 | if (strcmp(argv[2], "lora") != 0 && 827 | strcmp(argv[2], "fsk") != 0) 828 | return usage(argv[0]); 829 | 830 | ret = get_ifindex(argv[1], argv[2], &ifindex); 831 | if (ret < 0) 832 | return 1; 833 | //printf("ifindex %d\n", ifindex); 834 | 835 | sk = nl_socket_alloc(); 836 | if (sk == NULL) { 837 | fprintf(stderr, "nl_socket_alloc\n"); 838 | return 1; 839 | } 840 | 841 | ret = genl_connect(sk); 842 | if (ret < 0) { 843 | fprintf(stderr, "genl_connect\n"); 844 | nl_socket_free(sk); 845 | return 1; 846 | } 847 | 848 | if (strcmp(argv[2], "lora") == 0) { 849 | name = NLLORA_GENL_NAME; 850 | } else if (strcmp(argv[2], "fsk") == 0) { 851 | name = NLFSK_GENL_NAME; 852 | } else return 1; 853 | 854 | family_id = genl_ctrl_resolve(sk, name); 855 | if (family_id < 0) { 856 | fprintf(stderr, "genl_ctrl_resolve\n"); 857 | nl_socket_free(sk); 858 | return 1; 859 | } 860 | 861 | if (strcmp(argv[2], "lora") == 0) { 862 | ret = handle_lora(sk, family_id, ifindex, argv[3], argc - 4, &argv[4]); 863 | } else if (strcmp(argv[2], "fsk") == 0) { 864 | ret = handle_fsk(sk, family_id, ifindex, argv[3], argc - 4, &argv[4]); 865 | } else return 1; 866 | if (ret) { 867 | nl_socket_free(sk); 868 | return 1; 869 | } 870 | 871 | nl_socket_free(sk); 872 | 873 | return 0; 874 | } 875 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "include/linux/lora.h" 13 | 14 | #ifndef AF_LORA 15 | #define AF_LORA 28 16 | #endif 17 | 18 | #ifndef PF_LORA 19 | #define PF_LORA AF_LORA 20 | #endif 21 | 22 | int main(void) 23 | { 24 | int skt = socket(PF_LORA, SOCK_DGRAM, 1); 25 | if (skt == -1) { 26 | int err = errno; 27 | fprintf(stderr, "socket failed: %s\n", strerror(err)); 28 | return 1; 29 | } 30 | printf("socket %d\n", skt); 31 | 32 | struct ifreq ifr; 33 | strcpy(ifr.ifr_name, "lora0"); 34 | int ret = ioctl(skt, SIOCGIFINDEX, &ifr); 35 | if (ret == -1) { 36 | int err = errno; 37 | fprintf(stderr, "ioctl failed: %s\n", strerror(err)); 38 | return 1; 39 | } 40 | printf("ifindex %d\n", ifr.ifr_ifindex); 41 | 42 | struct sockaddr_lora addr; 43 | addr.lora_family = AF_LORA; 44 | addr.lora_ifindex = ifr.ifr_ifindex; 45 | ret = bind(skt, (struct sockaddr *)&addr, sizeof(addr)); 46 | if (ret == -1) { 47 | int err = errno; 48 | fprintf(stderr, "bind failed: %s\n", strerror(err)); 49 | return 1; 50 | } 51 | 52 | char buf[2]; 53 | buf[0] = 0x42; 54 | buf[1] = 0x43; 55 | int bytes_sent = write(skt, buf, 2); 56 | if (bytes_sent == -1) { 57 | int err = errno; 58 | fprintf(stderr, "write failed: %s\n", strerror(err)); 59 | return 1; 60 | } 61 | printf("bytes_sent %d\n", bytes_sent); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /txenocean.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef ARPHRD_ENOCEAN 15 | #define ARPHRD_ENOCEAN 832 16 | #endif 17 | 18 | #ifndef ETH_P_ERP2 19 | #define ETH_P_ERP2 0x0100 20 | #endif 21 | 22 | int main(void) 23 | { 24 | int skt = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ERP2)); 25 | if (skt == -1) { 26 | int err = errno; 27 | fprintf(stderr, "socket failed: %s\n", strerror(err)); 28 | return 1; 29 | } 30 | printf("socket %d\n", skt); 31 | 32 | struct ifreq ifr; 33 | strcpy(ifr.ifr_name, "enocean0"); 34 | int ret = ioctl(skt, SIOCGIFINDEX, &ifr); 35 | if (ret == -1) { 36 | int err = errno; 37 | fprintf(stderr, "ioctl failed: %s\n", strerror(err)); 38 | return 1; 39 | } 40 | printf("ifindex %d\n", ifr.ifr_ifindex); 41 | 42 | struct sockaddr_ll addr; 43 | memset(&addr, 0, sizeof(addr)); 44 | addr.sll_family = AF_PACKET; 45 | addr.sll_protocol = htons(ETH_P_ERP2); 46 | addr.sll_ifindex = ifr.ifr_ifindex; 47 | addr.sll_halen = 0; 48 | 49 | ret = bind(skt, (struct sockaddr *)&addr, sizeof(addr)); 50 | if (ret == -1) { 51 | int err = errno; 52 | fprintf(stderr, "bind failed: %s\n", strerror(err)); 53 | return 1; 54 | } 55 | 56 | char buf[15]; 57 | buf[0] = 0xD2; 58 | buf[1] = 0xDD; 59 | buf[2] = 0xDD; 60 | buf[3] = 0xDD; 61 | buf[4] = 0xDD; 62 | buf[5] = 0xDD; 63 | buf[6] = 0xDD; 64 | buf[7] = 0xDD; 65 | buf[8] = 0xDD; 66 | buf[9] = 0xDD; 67 | buf[10] = 0x00; 68 | buf[11] = 0x80; 69 | buf[12] = 0x35; 70 | buf[13] = 0xC4; 71 | buf[14] = 0x00; 72 | int bytes_sent = write(skt, buf, 15); 73 | if (bytes_sent == -1) { 74 | int err = errno; 75 | fprintf(stderr, "write failed: %s\n", strerror(err)); 76 | return 1; 77 | } 78 | printf("bytes_sent %d\n", bytes_sent); 79 | 80 | return 0; 81 | } 82 | --------------------------------------------------------------------------------