├── .gitignore ├── AUTHORS ├── LICENSE ├── Makefile ├── README.md ├── cc2530prog.c ├── gpio-sysfs.c └── gpio.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | cc2530prog 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Florian Fainelli 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Florian Fainelli . 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | This software is provided ``as is'' and any express or implied 17 | warranties, including, but not limited to, the implied warranties of 18 | merchantability and fitness for a particular purpose are 19 | disclaimed. In no event shall author or contributors be liable for any 20 | direct, indirect, incidental, special, exemplary, or consequential 21 | damages (including, but not limited to, procurement of substitute 22 | goods or services; loss of use, data, or profits; or business 23 | interruption) however caused and on any theory of liability, whether 24 | in contract, strict liability, or tort (including negligence or 25 | otherwise) arising in any way out of the use of this software, even if 26 | advised of the possibility of such damage. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for cc2530prog 3 | # 4 | 5 | CC?=gcc 6 | CFLAGS?= 7 | APP=cc2530prog 8 | GPIO_BACKEND?=gpio-sysfs 9 | 10 | all: $(APP) 11 | 12 | %.o: %.c 13 | $(CC) $(CFLAGS) -DGPIO_BACKEND=$(GPIO_BACKEND) -c $< -o $@ 14 | 15 | OBJS=$(APP).o $(GPIO_BACKEND).o 16 | 17 | $(APP): $(OBJS) 18 | $(CC) $(CFLAGS) $(OBJS) -o $@ 19 | 20 | clean: 21 | rm -f *.o $(APP) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cc2530prog - Texas Instruments CC2530 Micro controller programming utility 2 | 3 | ## 1. General informations 4 | 5 | This utility uses the CC2530 Debug Port to program the micro-controller. The 6 | specific details of this interface are described in the following documents: 7 | - [swru191b](http://www.ti.com/lit/swru191) 8 | - [swra124](http://www.ti.com/lit/ug/swra124/swra124.pdf) 9 | 10 | The hardware is programmed with the means of 3 GPIOs: 11 | - reset (RST) (whose polarity can be software configured) 12 | - data (DATA) 13 | - clock (CLK) 14 | 15 | The principle is the following: 16 | - pulse the reset line to enter debug mode 17 | - configure the hardware with DMA descriptors for transfering data 18 | from the DEBUG port directly to Flash 19 | - clock out data to the debug port using the GPIOs 20 | 21 | ## 2. Software integration and modifications 22 | 23 | The current version is provided using the Linux GPIO sysfs interface. Linux 24 | can expose GPIOs to the user-space under **/sys/class/gpio** when the config 25 | symbol **CONFIG_GPIO_SYSFS=y** is enabled. 26 | 27 | 3 GPIOs must be exposed by your hardware in order to use cc2530prog. In case 28 | your system does not use GPIOs exposed through sysfs, you are supposed to 29 | implement the following functions (also declared in gpio.h): 30 | 31 | **gpio_export**: exports a GPIO pin for an user-space/consumer/producer application 32 | 33 | **gpio_unexport**: unexports a GPIO pin 34 | 35 | **gpio_set_direction**: set the direction (IN, OUT, HIGHZ) of the given pin 36 | 37 | **gpio_get_value**: sets the output value of a given pin (pin must be output first) 38 | 39 | **gpio_set_value**: returns the current gpio value 40 | 41 | You are then supposed to set the Makefile environment **GPIO_BACKEND** to point 42 | to the file implementing these GPIO routines for your specific platform. 43 | 44 | ## 3. Recommandations 45 | 46 | The CC2530 firmware size matches the available hardware flash sizes (64KB up to 47 | 256KB) but since programming using the debug port is very slow, it is 48 | recommended to bootstrap using the debug port and then use another mechanism to 49 | transfer the bigger software image. 50 | 51 | TI provides such a mechanism using an UART/SPI bootloader. 52 | 53 | ## 4. Future developments 54 | 55 | At the moment nothing else is planned for this utility, however it should be 56 | possible to use the routines exposed by this utility for doing interactive 57 | debugging using the debug interface (which is what the CC2530 Debug dongles 58 | basically do). 59 | 60 | -- 61 | Florian Fainelli 62 | -------------------------------------------------------------------------------- /cc2530prog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cc2530prog - Texas Instruments CC2530 programming tool 3 | * 4 | * Copyright (C) 2010, Florian Fainelli 5 | * 6 | * This file is part of "cc2530prog", this file is distributed under 7 | * a 2-clause BSD license, see LICENSE for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "gpio.h" 24 | 25 | #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x[0]))) 26 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) 27 | 28 | static unsigned int gpios[3] = { RST_GPIO, CCLK_GPIO, DATA_GPIO }; 29 | 30 | struct cc2530_cmd { 31 | char name[32]; 32 | uint8_t id; 33 | uint8_t in; 34 | uint8_t out; 35 | }; 36 | 37 | static unsigned debug_enabled; 38 | static unsigned verbose, progress; 39 | 40 | #define DEFAULT_TIMEOUT 1000 41 | 42 | #define CMD_ERASE 0x10 43 | #define CMD_WR_CFG 0x18 44 | #define CMD_RD_CFG 0x20 45 | #define CMD_GET_PC 0x28 46 | #define CMD_RD_ST 0x30 47 | #define CMD_SET_BRK 0x38 48 | #define CMD_HALT 0x40 49 | #define CMD_RESUME 0x48 50 | #define CMD_DBG_INST 0x50 51 | #define CMD_STEP_INST 0x58 52 | #define CMD_GET_BM 0x60 53 | #define CMD_GET_CHIP 0x68 54 | #define CMD_BURST_WR 0x80 55 | 56 | /* Various chip statuses */ 57 | #define STACK_OVF 0x01 58 | #define OSC_STABLE 0x02 59 | #define DBG_LOCKED 0x04 60 | #define HALT_STATUS 0x08 61 | #define PWR_MODE_0 0x10 62 | #define CPU_HALTED 0x20 63 | #define PCON_IDLE 0x40 64 | #define CHIP_ERASE_BSY 0x80 65 | 66 | #define FCTL_BUSY 0x80 67 | 68 | /* IDs that we recognize */ 69 | #define CC2530_ID 0xA5 70 | 71 | /* Buffers */ 72 | #define ADDR_BUF0 0x0000 /* 1K */ 73 | #define ADDR_BUF1 0x0400 /* 1K */ 74 | #define ADDR_DMA_DESC 0x0800 /* 32 bytes */ 75 | 76 | /* DMA Channels */ 77 | #define CH_DBG_TO_BUF0 0x02 78 | #define CH_DBG_TO_BUF1 0x04 79 | #define CH_BUF0_TO_FLASH 0x08 80 | #define CH_BUF1_TO_FLASH 0x10 81 | 82 | #define PROG_BLOCK_SIZE 1024 83 | 84 | #define LOBYTE(w) ((uint8_t)(w)) 85 | #define HIBYTE(w) ((uint8_t)(((uint16_t)(w) >> 8) & 0xFF)) 86 | 87 | /* 88 | * Register offsets from ioCC2530.h 89 | * make sure that these are always extended registers (16-bits addr) 90 | * otherwise this simply will not work. 91 | */ 92 | #define X_EXT_ADDR_BASE 0x616A 93 | #define DBGDATA 0x6260 94 | #define FCTL 0x6270 95 | #define FADDRL 0x6271 96 | #define FADDRH 0x6272 97 | #define FWDATA 0x6273 98 | #define X_CHIPINFO0 0x6276 99 | #define X_CHIPINFO1 0x6277 100 | 101 | #define X_MEMCTR 0x70C7 102 | #define X_DMA1CFGH 0x70D3 103 | #define X_DMA1CFGL 0x70D4 104 | #define X_DMAARM 0x70D6 105 | 106 | #define X_CLKCONCMD 0x70C6 107 | #define X_CLKCONSTA 0x709E 108 | 109 | const uint8_t dma_desc[32] = { 110 | /* Debug Interface -> Buffer 0 (Channel 1) */ 111 | HIBYTE(DBGDATA), /* src[15:8] */ 112 | LOBYTE(DBGDATA), /* src[7:0] */ 113 | HIBYTE(ADDR_BUF0), /* dest[15:8] */ 114 | LOBYTE(ADDR_BUF0), /* dest[7:0] */ 115 | HIBYTE(PROG_BLOCK_SIZE), 116 | LOBYTE(PROG_BLOCK_SIZE), 117 | 31, /* trigger DBG_BW */ 118 | 0x11, /* increment destination */ 119 | 120 | /* Debug Interface -> Buffer 1 (Channel 2) */ 121 | HIBYTE(DBGDATA), /* src[15:8] */ 122 | LOBYTE(DBGDATA), /* src[7:0] */ 123 | HIBYTE(ADDR_BUF1), /* dest[15:8] */ 124 | LOBYTE(ADDR_BUF1), /* dest[7:0] */ 125 | HIBYTE(PROG_BLOCK_SIZE), 126 | LOBYTE(PROG_BLOCK_SIZE), 127 | 31, /* trigger DBG_BW */ 128 | 0x11, /* increment destination */ 129 | 130 | /* Buffer 0 -> Flash controller (Channel 3) */ 131 | HIBYTE(ADDR_BUF0), /* src[15:8] */ 132 | LOBYTE(ADDR_BUF0), /* src[7:0] */ 133 | HIBYTE(FWDATA), /* dest[15:8] */ 134 | LOBYTE(FWDATA), /* dest[7:0] */ 135 | HIBYTE(PROG_BLOCK_SIZE), 136 | LOBYTE(PROG_BLOCK_SIZE), 137 | 18, /* trigger FLASH */ 138 | 0x42, /* increment source */ 139 | 140 | /* Buffer 1 -> Flash controller (Channel 4) */ 141 | HIBYTE(ADDR_BUF1), /* src[15:8] */ 142 | LOBYTE(ADDR_BUF1), /* src[7:0] */ 143 | HIBYTE(FWDATA), /* dest[15:8] */ 144 | LOBYTE(FWDATA), /* dest[7:0] */ 145 | HIBYTE(PROG_BLOCK_SIZE), 146 | LOBYTE(PROG_BLOCK_SIZE), 147 | 18, /* trigger FLASH */ 148 | 0x42 /* increment source */ 149 | }; 150 | 151 | static uint16_t flash_ptr = 0; 152 | 153 | static void init_flash_ptr(void) 154 | { 155 | flash_ptr = 0; 156 | } 157 | 158 | static unsigned char *fwdata; 159 | 160 | static inline uint8_t get_next_flash_byte(void) 161 | { 162 | return fwdata[flash_ptr++]; 163 | } 164 | 165 | static inline void bytes_to_bits(uint8_t byte) 166 | { 167 | int i; 168 | 169 | for (i = 0; i < 8; i++) { 170 | if (byte & (1 << i)) 171 | printf("1"); 172 | else 173 | printf("0"); 174 | } 175 | } 176 | 177 | static struct cc2530_cmd cc2530_commands[] = { 178 | { 179 | .name = "erase", 180 | .id = CMD_ERASE, 181 | .in = 0, 182 | .out = 1, 183 | }, { 184 | .name = "write_config", 185 | .id = CMD_WR_CFG, 186 | .in = 1, 187 | .out = 1, 188 | }, { 189 | .name = "read_config", 190 | .id = CMD_RD_CFG, 191 | .in = 0, 192 | .out = 1, 193 | }, { 194 | .name = "get_pc", 195 | .id = CMD_GET_PC, 196 | .in = 0, 197 | .out = 2, 198 | }, { 199 | .name = "read_status", 200 | .id = CMD_RD_ST, 201 | .in = 0, 202 | .out = 1, 203 | }, { 204 | .name = "halt", 205 | .id = CMD_HALT, 206 | .in = 0, 207 | .out = 1, 208 | }, { 209 | .name = "resume", 210 | .id = CMD_RESUME, 211 | .in = 0, 212 | .out = 1, 213 | }, { 214 | .name = "debug_inst", 215 | .id = CMD_DBG_INST, 216 | .in = -1, /* variable */ 217 | .out = 1, 218 | }, { 219 | .name = "step_inst", 220 | .id = CMD_STEP_INST, 221 | .in = 0, 222 | .out = 1, 223 | }, { 224 | .name = "get_bm", 225 | .id = CMD_GET_BM, 226 | .in = 0, 227 | .out = 1, 228 | }, { 229 | .name = "get_chip_id", 230 | .id = CMD_GET_CHIP, 231 | .in = 0, 232 | .out = 2, 233 | }, { 234 | .name = "burst_write", 235 | .id = CMD_BURST_WR, 236 | .in = -1, /* variable */ 237 | .out = 1, 238 | }, 239 | }; 240 | 241 | static inline struct cc2530_cmd *find_cmd_by_name(const char *name) 242 | { 243 | int len; 244 | unsigned int i; 245 | 246 | for (i = 0; i < ARRAY_SIZE(cc2530_commands); i++) { 247 | len = strlen(name); 248 | if (!strncmp(cc2530_commands[i].name, name, len)) 249 | return &cc2530_commands[i]; 250 | } 251 | 252 | return NULL; 253 | } 254 | 255 | static void cc2530_show_command_list(void) 256 | { 257 | unsigned int i; 258 | 259 | printf("Supported commands:\n"); 260 | for (i = 0; i < ARRAY_SIZE(cc2530_commands); i++) 261 | printf("\t%s\n", cc2530_commands[i].name); 262 | } 263 | 264 | /* 265 | * Perform GPIO initialization 266 | */ 267 | static int cc2530_gpio_init(void) 268 | { 269 | int ret; 270 | unsigned int i; 271 | 272 | for (i = 0; i < ARRAY_SIZE(gpios); i++) { 273 | ret = gpio_export(gpios[i]); 274 | if (ret) { 275 | fprintf(stderr, "failed to export %d\n", gpios[i]); 276 | return ret; 277 | } 278 | 279 | ret = gpio_set_direction(gpios[i], GPIO_DIRECTION_OUT); 280 | if (ret) { 281 | fprintf(stderr, "failed to set direction on %d\n", gpios[i]); 282 | return ret; 283 | } 284 | } 285 | 286 | return 0; 287 | } 288 | 289 | /* 290 | * Put back GPIOs in a sane state 291 | */ 292 | static int cc2530_gpio_deinit(void) 293 | { 294 | int ret; 295 | unsigned int i; 296 | 297 | for (i = 0; i < ARRAY_SIZE(gpios); i++) { 298 | ret = gpio_set_direction(gpios[i], GPIO_DIRECTION_IN); 299 | if (ret) { 300 | fprintf(stderr, "failed to set direction on %d\n", gpios[i]); 301 | return ret; 302 | } 303 | 304 | ret = gpio_unexport(gpios[i]); 305 | if (ret) { 306 | fprintf(stderr, "failed to unexport %d\n", gpios[i]); 307 | return ret; 308 | } 309 | } 310 | 311 | return 0; 312 | } 313 | 314 | /* 315 | * Hold reset low while raising clock twice 316 | */ 317 | static int cc2530_enter_debug(void) 318 | { 319 | int i; 320 | 321 | /* pulse RST low */ 322 | gpio_set_value(RST_GPIO, RST_GPIO_POL 0); 323 | 324 | for (i = 0; i < 2; i++) { 325 | gpio_set_value(CCLK_GPIO, 0); 326 | gpio_set_value(CCLK_GPIO, 1); 327 | } 328 | 329 | /* Keep clock low */ 330 | gpio_set_value(CCLK_GPIO, 0); 331 | 332 | /* pulse Reset high */ 333 | gpio_set_value(RST_GPIO, RST_GPIO_POL 1); 334 | 335 | debug_enabled = 1; 336 | 337 | return 0; 338 | } 339 | 340 | static int cc2530_leave_debug(void) 341 | { 342 | gpio_set_value(RST_GPIO, RST_GPIO_POL 0); 343 | gpio_set_value(RST_GPIO, RST_GPIO_POL 1); 344 | 345 | return 0; 346 | } 347 | 348 | /* 349 | * Bit-bang a byte on the GPIO data line 350 | */ 351 | static inline void send_byte(unsigned char byte) 352 | { 353 | int i; 354 | 355 | /* Data setup on rising clock edge */ 356 | for (i = 7; i >= 0; i--) { 357 | if (byte & (1 << i)) 358 | gpio_set_value(DATA_GPIO, 1); 359 | else 360 | gpio_set_value(DATA_GPIO, 0); 361 | gpio_set_value(CCLK_GPIO, 1); 362 | gpio_set_value(CCLK_GPIO, 0); 363 | } 364 | } 365 | 366 | /* 367 | * Clock in a byte from the GPIO line 368 | */ 369 | static inline void read_byte(unsigned char *byte) 370 | { 371 | int i; 372 | bool val; 373 | *byte = 0; 374 | 375 | /* data read on falling clock edge */ 376 | for (i = 7; i >= 0; i--) { 377 | gpio_set_value(CCLK_GPIO, 1); 378 | gpio_get_value(DATA_GPIO, &val); 379 | if (val) 380 | *byte |= (1 << i); 381 | gpio_set_value(CCLK_GPIO, 0); 382 | } 383 | } 384 | 385 | /* 386 | * Send the command to the chip 387 | */ 388 | static int cc2530_do_cmd(const struct cc2530_cmd *cmd, unsigned char *params, unsigned char *outbuf) 389 | { 390 | int ret; 391 | int bytes; 392 | unsigned int timeout = DEFAULT_TIMEOUT; 393 | bool val; 394 | unsigned char *answer; 395 | 396 | if (!cmd) { 397 | fprintf(stderr, "invalid command\n"); 398 | return -1; 399 | } 400 | 401 | /* allocate as many bytes as we need to store the result */ 402 | answer = malloc(cmd->out); 403 | if (!answer) { 404 | perror("malloc"); 405 | return -1; 406 | } 407 | memset(answer, 0, cmd->out); 408 | 409 | ret = gpio_set_direction(DATA_GPIO, GPIO_DIRECTION_OUT); 410 | if (ret) { 411 | fprintf(stderr, "failed to put gpio in output direction\n"); 412 | goto out_exit; 413 | } 414 | 415 | /* 416 | * Debug instruction also needs to set the number of bytes 417 | * of the sent instruction. 418 | */ 419 | if (cmd->id == CMD_DBG_INST) 420 | send_byte(cmd->id | cmd->in); 421 | else 422 | send_byte(cmd->id); 423 | 424 | /* If there is any command payload also send it */ 425 | for (bytes = 0; bytes < cmd->in; bytes++) 426 | send_byte(params[bytes]); 427 | 428 | /* Now change the pin direction and wait for the chip to be ready 429 | * and sample the data pin until the chip is ready to answer */ 430 | ret = gpio_set_direction(DATA_GPIO, GPIO_DIRECTION_IN); 431 | if (ret) { 432 | fprintf(stderr, "failed to put back gpio in input direction\n"); 433 | goto out_exit; 434 | } 435 | 436 | /* 437 | * Cope with commands requiring a response delay and those which do 438 | * not. In case the data line was not low right after we wrote data to 439 | * clock out the chip 8 times as per the specification mentions. The 440 | * data line should then be low and we are ready to read out from the 441 | * chip. 442 | */ 443 | gpio_get_value(DATA_GPIO, &val); 444 | while (val && timeout--) { 445 | for (bytes = 0; bytes < 8; bytes++) { 446 | gpio_set_value(CCLK_GPIO, 1); 447 | gpio_set_value(CCLK_GPIO, 0); 448 | } 449 | gpio_get_value(DATA_GPIO, &val); 450 | } 451 | 452 | if (!timeout) { 453 | fprintf(stderr, "timed out waiting for chip to be ready again\n"); 454 | goto out_exit; 455 | } 456 | 457 | /* Now read the answer */ 458 | for (bytes = 0; bytes < cmd->out; bytes++) 459 | read_byte(&answer[bytes]); 460 | 461 | memcpy(outbuf, answer, cmd->out); 462 | out_exit: 463 | free(answer); 464 | return ret; 465 | } 466 | 467 | /* 468 | * Bypass the command sending infrastructure to allow 469 | * direct access to the what is sent to the chip 470 | */ 471 | static int cc2530_burst_write(void) 472 | { 473 | int ret; 474 | uint16_t i; 475 | unsigned char result; 476 | unsigned int timeout = DEFAULT_TIMEOUT; 477 | bool val; 478 | 479 | ret = gpio_set_direction(DATA_GPIO, GPIO_DIRECTION_OUT); 480 | if (ret) { 481 | fprintf(stderr, "failed to put gpio in output direction\n"); 482 | return ret; 483 | } 484 | 485 | send_byte(CMD_BURST_WR | HIBYTE(PROG_BLOCK_SIZE)); 486 | send_byte(LOBYTE(PROG_BLOCK_SIZE)); 487 | 488 | for (i = 0; i < PROG_BLOCK_SIZE; i++) 489 | send_byte(get_next_flash_byte()); 490 | 491 | ret = gpio_set_direction(DATA_GPIO, GPIO_DIRECTION_IN); 492 | if (ret) { 493 | fprintf(stderr, "failed to put gpio in input direction\n"); 494 | return ret; 495 | } 496 | 497 | gpio_get_value(DATA_GPIO, &val); 498 | while (val && timeout--) { 499 | for (i = 0; i < 8; i++) { 500 | gpio_set_value(CCLK_GPIO, 1); 501 | gpio_set_value(CCLK_GPIO, 0); 502 | } 503 | gpio_get_value(DATA_GPIO, &val); 504 | } 505 | 506 | if (!timeout) { 507 | fprintf(stderr, "timed out waiting for chip to be ready\n"); 508 | return -1; 509 | } 510 | 511 | read_byte(&result); 512 | 513 | return 0; 514 | } 515 | 516 | static int cc2530_chip_erase(struct cc2530_cmd *cmd) 517 | { 518 | int ret; 519 | unsigned char result; 520 | unsigned int timeout = DEFAULT_TIMEOUT; 521 | 522 | cmd = find_cmd_by_name("erase"); 523 | ret = cc2530_do_cmd(cmd, NULL, &result); 524 | if (ret) { 525 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 526 | return ret; 527 | } 528 | 529 | cmd = find_cmd_by_name("read_status"); 530 | do { 531 | ret = cc2530_do_cmd(cmd, NULL, &result); 532 | if (ret) { 533 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 534 | return ret; 535 | } 536 | usleep(10); 537 | } while ((result & CHIP_ERASE_BSY) && timeout--); 538 | 539 | if (!timeout) { 540 | fprintf(stderr, "timeout waiting for the chip to be erased\n"); 541 | return -1; 542 | } 543 | 544 | return 0; 545 | } 546 | 547 | static int cc2530_write_xdata_memory(struct cc2530_cmd *cmd, uint16_t addr, uint8_t value) 548 | { 549 | int ret; 550 | unsigned char instr[3]; 551 | unsigned char result; 552 | 553 | cmd = find_cmd_by_name("debug_inst"); 554 | instr[0] = 0x90; 555 | instr[1] = HIBYTE(addr); 556 | instr[2] = LOBYTE(addr); 557 | cmd->in = sizeof(instr); 558 | 559 | ret = cc2530_do_cmd(cmd, instr, &result); 560 | if (ret) { 561 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 562 | return ret; 563 | } 564 | 565 | instr[0] = 0x74; 566 | instr[1] = value; 567 | cmd->in = 2; 568 | 569 | ret = cc2530_do_cmd(cmd, instr, &result); 570 | if (ret) { 571 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 572 | return ret; 573 | } 574 | 575 | instr[0] = 0xF0; 576 | cmd->in = 1; 577 | ret = cc2530_do_cmd(cmd, instr, &result); 578 | if (ret) { 579 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 580 | return ret; 581 | } 582 | 583 | return 0; 584 | } 585 | 586 | static int cc2530_read_xdata_memory(struct cc2530_cmd *cmd, uint16_t addr, unsigned char *result) 587 | { 588 | int ret; 589 | unsigned char instr[3]; 590 | unsigned char res; 591 | 592 | cmd = find_cmd_by_name("debug_inst"); 593 | instr[0] = 0x90; 594 | instr[1] = HIBYTE(addr); 595 | instr[2] = LOBYTE(addr); 596 | cmd->in = sizeof(instr); 597 | 598 | ret = cc2530_do_cmd(cmd, instr, result); 599 | if (ret) { 600 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 601 | return ret; 602 | } 603 | 604 | instr[0] = 0xE0; 605 | cmd->in = 1; 606 | 607 | ret = cc2530_do_cmd(cmd, instr, &res); 608 | if (ret) { 609 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 610 | return ret; 611 | } 612 | memcpy(result, &res, sizeof(res)); 613 | 614 | return 0; 615 | } 616 | 617 | static int cc2530_write_xdata_memory_block(struct cc2530_cmd *cmd, 618 | uint16_t addr, const uint8_t *values, uint16_t num_bytes) 619 | { 620 | int ret; 621 | unsigned char instr[3]; 622 | unsigned char result; 623 | uint16_t i; 624 | 625 | cmd = find_cmd_by_name("debug_inst"); 626 | instr[0] = 0x90; 627 | instr[1] = HIBYTE(addr); 628 | instr[2] = LOBYTE(addr); 629 | cmd->in = sizeof(instr); 630 | 631 | ret = cc2530_do_cmd(cmd, instr, &result); 632 | if (ret) { 633 | fprintf(stderr, "failed to issue: %s\n", cmd->name); 634 | return ret; 635 | } 636 | 637 | for (i = 0; i < num_bytes; i++) { 638 | instr[0] = 0x74; 639 | instr[1] = values[i]; 640 | cmd->in = 2; 641 | 642 | ret = cc2530_do_cmd(cmd, instr, &result); 643 | if (ret) { 644 | fprintf(stderr, "failed to issue: %s at %i\n", cmd->name, i); 645 | return ret; 646 | } 647 | 648 | instr[0] = 0xF0; 649 | cmd->in = 1; 650 | ret = cc2530_do_cmd(cmd, instr, &result); 651 | if (ret) { 652 | fprintf(stderr, "failed to issue: %s at %i\n", cmd->name, i); 653 | return ret; 654 | } 655 | 656 | instr[0] = 0xA3; 657 | cmd->in = 1; 658 | ret = cc2530_do_cmd(cmd, instr, &result); 659 | if (ret) { 660 | fprintf(stderr, "failed to issue: %s at %i\n", cmd->name, i); 661 | return ret; 662 | } 663 | } 664 | 665 | return 0; 666 | } 667 | 668 | static uint32_t cc2530_flash_verify(struct cc2530_cmd *cmd, uint32_t max_addr) 669 | { 670 | uint8_t bank; 671 | unsigned char instr[3]; 672 | unsigned char result; 673 | unsigned char expected; 674 | uint16_t i; 675 | uint32_t addr = 0; 676 | int ret; 677 | 678 | for (bank = 0; bank < 8; bank++) { 679 | if (verbose) 680 | printf("Reading bank: %d\n", bank); 681 | 682 | ret = cc2530_write_xdata_memory(cmd, X_MEMCTR, bank); 683 | if (ret) { 684 | fprintf(stderr, "%s: failed to write to X_MEMCTR\n", __func__); 685 | return ret; 686 | } 687 | 688 | cmd = find_cmd_by_name("debug_inst"); 689 | instr[0] = 0x90; 690 | instr[1] = 0x80; 691 | instr[2] = 0x00; 692 | cmd->in = sizeof(instr); 693 | 694 | ret = cc2530_do_cmd(cmd, instr, &result); 695 | if (ret) { 696 | fprintf(stderr, "%s: command failed: %s\n", __func__, cmd->name); 697 | return ret; 698 | } 699 | 700 | for (i = 0; i < 32*1024; i++) { 701 | if (addr == max_addr) 702 | return addr; 703 | 704 | instr[0] = 0xE0; 705 | cmd->in = 1; 706 | ret = cc2530_do_cmd(cmd, instr, &result); 707 | if (ret) { 708 | fprintf(stderr, "%s: command failed at %i\n", __func__, i); 709 | return ret; 710 | } 711 | 712 | expected = get_next_flash_byte(); 713 | if (result != expected) { 714 | printf("[bank%d][%d], result: %02x, expected: %02x\n", 715 | bank, i, result, expected); 716 | } 717 | 718 | instr[0] = 0xA3; 719 | cmd->in = 1; 720 | ret = cc2530_do_cmd(cmd, instr, &result); 721 | if (ret) { 722 | fprintf(stderr, "%s: command failed at %i\n", __func__, i); 723 | return ret; 724 | } 725 | addr++; 726 | } 727 | } 728 | 729 | return addr; 730 | } 731 | 732 | static uint8_t cc2530_program_flash(struct cc2530_cmd *cmd, uint16_t num_buffers) 733 | { 734 | uint8_t dbg_arm, flash_arm; 735 | uint8_t max_speed = 1; 736 | uint16_t i; 737 | uint8_t wait; 738 | unsigned char result; 739 | int ret; 740 | unsigned int timeout = DEFAULT_TIMEOUT; 741 | 742 | /* Write the 4 DMA descriptors */ 743 | ret = cc2530_write_xdata_memory_block(cmd, ADDR_DMA_DESC, dma_desc, ARRAY_SIZE(dma_desc)); 744 | if (ret) { 745 | fprintf(stderr, "%s: failed to write DMA descriptors\n", __func__); 746 | return ret; 747 | } 748 | 749 | /* Set the pointer to the DMA descriptors */ 750 | ret = cc2530_write_xdata_memory(cmd, X_DMA1CFGH, HIBYTE(ADDR_DMA_DESC)); 751 | if (ret) { 752 | fprintf(stderr, "%s: failed to set DMA descriptors (part 1)\n", __func__); 753 | return ret; 754 | } 755 | ret = cc2530_write_xdata_memory(cmd, X_DMA1CFGL, LOBYTE(ADDR_DMA_DESC)); 756 | if (ret) { 757 | fprintf(stderr, "%s: failed to set DMA descriptors (part 2)\n", __func__); 758 | return ret; 759 | } 760 | 761 | /* Make sure FADDR is 0x0000 */ 762 | ret = cc2530_write_xdata_memory(cmd, FADDRH, 0); 763 | if (ret) { 764 | fprintf(stderr, "%s: failed to set FADDRH\n", __func__); 765 | return ret; 766 | } 767 | ret = cc2530_write_xdata_memory(cmd, FADDRL, 0); 768 | if (ret) { 769 | fprintf(stderr, "%s: failed to set FADDRL\n", __func__); 770 | return ret; 771 | } 772 | 773 | for (i = 0; i < num_buffers; i++) { 774 | if (progress) { 775 | printf("%d/%d\n", i, num_buffers - 1); 776 | fflush(stdout); 777 | } 778 | /* Set what is to be written to DMAARM, based on loop iteration */ 779 | if ((i & 0x0001) == 0) { 780 | dbg_arm = CH_DBG_TO_BUF0; 781 | flash_arm = CH_BUF0_TO_FLASH; 782 | } else { 783 | dbg_arm = CH_DBG_TO_BUF1; 784 | flash_arm = CH_BUF1_TO_FLASH; 785 | } 786 | 787 | /* transfer next buffer (first buffer when i == 0) */ 788 | ret = cc2530_write_xdata_memory(cmd, X_DMAARM, dbg_arm); 789 | if (ret) { 790 | fprintf(stderr, "%s: failed to arm DMA\n", __func__); 791 | return ret; 792 | } 793 | cc2530_burst_write(); 794 | 795 | /* wait for write to finish */ 796 | wait = 0; 797 | do { 798 | ret = cc2530_read_xdata_memory(cmd, FCTL, &result); 799 | if (ret) { 800 | fprintf(stderr, "%s: failed at %i\n", __func__, i); 801 | return ret; 802 | } 803 | wait = 1; 804 | } while ((result & FCTL_BUSY) && timeout--); 805 | 806 | if (!timeout) { 807 | fprintf(stderr, "%s: timeout at %i\n", __func__, i); 808 | return -1; 809 | } 810 | 811 | /* no waiting means programming finished before burst write */ 812 | if (i > 0 && wait == 0) 813 | max_speed = 0; 814 | 815 | /* start programming current buffer */ 816 | ret = cc2530_write_xdata_memory(cmd, X_DMAARM, flash_arm); 817 | if (ret) { 818 | fprintf(stderr, "%s: failed programming current buffer: %d\n", __func__, i); 819 | return ret; 820 | } 821 | ret = cc2530_write_xdata_memory(cmd, FCTL, 0x06); 822 | if (ret) { 823 | fprintf(stderr, "%s: failed to set FCTL\n", __func__); 824 | return ret; 825 | } 826 | } 827 | 828 | timeout = DEFAULT_TIMEOUT; 829 | 830 | /* Programming last buffer in progress, wait until done */ 831 | do { 832 | ret = cc2530_read_xdata_memory(cmd, FCTL, &result); 833 | if (ret) { 834 | fprintf(stderr, "%s: failed\n", __func__); 835 | return ret; 836 | } 837 | } while ((result & FCTL_BUSY) && timeout--); 838 | 839 | if (!timeout) { 840 | fprintf(stderr, "%s: timeout programming last buffer\n", __func__); 841 | return -1; 842 | } 843 | 844 | return max_speed; 845 | } 846 | 847 | static int cc2530_chip_identify(struct cc2530_cmd *cmd, int *flash_size) 848 | { 849 | int ret = 0; 850 | unsigned char result[2] = { 0 }; 851 | unsigned char ext_addr[8] = { 0 }; 852 | int i; 853 | 854 | ret = gpio_set_direction(DATA_GPIO, GPIO_DIRECTION_OUT); 855 | if (ret) { 856 | fprintf(stderr, "failed to set data gpio direction\n"); 857 | return ret; 858 | } 859 | 860 | cmd = find_cmd_by_name("get_chip_id"); 861 | ret = cc2530_do_cmd(cmd, NULL, result); 862 | if (ret) { 863 | fprintf(stderr, "%s: failed to issue: %s\n", __func__, cmd->name); 864 | goto out; 865 | } 866 | 867 | /* Check that we actually know that chip */ 868 | if (result[0] != CC2530_ID) { 869 | fprintf(stderr, "unknown Chip ID: %02x\n", result[0]); 870 | if (result[0] == 0xFF || result[0] == 0) 871 | fprintf(stderr, "someone is holding the CLK/DATA lines against us " 872 | "make sure no debugger is *connected*\n"); 873 | ret = -EINVAL; 874 | goto out; 875 | } 876 | 877 | if (verbose) 878 | printf("Texas Instruments CC2530 (ID: 0x%02x, rev 0x%02x)\n", result[0], result[1]); 879 | /* 880 | * Do some chip identification 881 | */ 882 | for (i = 0; i < 7; i++) { 883 | ret = cc2530_read_xdata_memory(cmd, X_EXT_ADDR_BASE + i, result); 884 | if (ret) { 885 | fprintf(stderr, "%s: failed to read X_ETXADDR%d\n", __func__, i); 886 | return ret; 887 | } 888 | ext_addr[i] = result[0]; 889 | } 890 | 891 | if (verbose) 892 | printf("Extended addr: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 893 | ext_addr[7], ext_addr[6], ext_addr[5], ext_addr[4], 894 | ext_addr[3], ext_addr[2], ext_addr[1], ext_addr[0]); 895 | 896 | ret = cc2530_read_xdata_memory(cmd, X_CHIPINFO0, result); 897 | if (ret) { 898 | fprintf(stderr, "failed to read X_CHIPINFO0 register\n"); 899 | return ret; 900 | } 901 | 902 | if (verbose) { 903 | if (result[0] & 8) 904 | printf("USB available\n"); 905 | else 906 | printf("USB: not availabe\n"); 907 | } 908 | 909 | switch ((result[0] & 0x70) >> 4) { 910 | case 1: 911 | *flash_size = 32; 912 | break; 913 | case 2: 914 | *flash_size = 64; 915 | break; 916 | case 3: 917 | *flash_size = 128; 918 | break; 919 | case 4: 920 | *flash_size = 256; 921 | } 922 | 923 | if (verbose) 924 | printf("Flash size: %d KB\n", *flash_size); 925 | 926 | *flash_size *= 1024; 927 | 928 | ret = cc2530_read_xdata_memory(cmd, X_CHIPINFO1, result); 929 | if (ret) { 930 | fprintf(stderr, "failed to read X_CHIPINFO1 register\n"); 931 | return ret; 932 | } 933 | out: 934 | return ret; 935 | } 936 | 937 | /* 938 | * Perform full CC2530 chip initialization and programming 939 | */ 940 | static int cc2530_do_program(struct cc2530_cmd *cmd, off_t fwsize, unsigned do_readback) 941 | { 942 | int ret = 0; 943 | unsigned char config; 944 | unsigned char result; 945 | uint32_t num_bytes_ok = 0; 946 | uint16_t blocks; 947 | unsigned int timeout = DEFAULT_TIMEOUT; 948 | unsigned int retry_cnt = 3; 949 | 950 | while (retry_cnt--) { 951 | /* Enable DMA */ 952 | cmd = find_cmd_by_name("write_config"); 953 | config = 0x22; 954 | ret = cc2530_do_cmd(cmd, &config, &result); 955 | if (ret) { 956 | fprintf(stderr, "failed to enable DMA\n"); 957 | return ret; 958 | } 959 | 960 | if (result != config) { 961 | fprintf(stderr, "write config failed (retry count: %d)\n", retry_cnt); 962 | 963 | cc2530_enter_debug(); 964 | continue; 965 | } 966 | 967 | break; 968 | } 969 | 970 | ret = cc2530_write_xdata_memory(cmd, X_CLKCONCMD, 0x80); 971 | if (ret) { 972 | fprintf(stderr, "failed to write X_CLKCONCMD\n"); 973 | return ret; 974 | } 975 | 976 | do { 977 | ret = cc2530_read_xdata_memory(cmd, X_CLKCONSTA, &result); 978 | if (ret) { 979 | fprintf(stderr, "%s: failed to read X_CLKCONSTA\n", __func__); 980 | return ret; 981 | } 982 | } while ((result != 0x80) && timeout--); 983 | 984 | if (!timeout) { 985 | fprintf(stderr, "%s: timeout waiting for CLKCONSTA\n", __func__); 986 | return -1; 987 | } 988 | 989 | ret = cc2530_chip_erase(cmd); 990 | if (ret) { 991 | fprintf(stderr, "failed to erase chip\n"); 992 | return ret; 993 | } 994 | 995 | init_flash_ptr(); 996 | 997 | blocks = DIV_ROUND_UP(fwsize, PROG_BLOCK_SIZE); 998 | 999 | ret = cc2530_program_flash(cmd, blocks); 1000 | if (ret && verbose) 1001 | printf("Programmed at maximum speed\n"); 1002 | 1003 | if (!do_readback) 1004 | goto cc2530_reset_mcu; 1005 | 1006 | init_flash_ptr(); 1007 | 1008 | num_bytes_ok = cc2530_flash_verify(cmd, blocks * PROG_BLOCK_SIZE); 1009 | if (num_bytes_ok == (blocks * PROG_BLOCK_SIZE)) { 1010 | if (verbose) 1011 | printf("Verification OK\n"); 1012 | goto cc2530_reset_mcu; 1013 | } else 1014 | if (verbose) 1015 | printf("Verification failed\n"); 1016 | 1017 | cc2530_reset_mcu: 1018 | cc2530_leave_debug(); 1019 | 1020 | return 0; 1021 | } 1022 | 1023 | static int cc2530_oneshot_command(struct cc2530_cmd *cmd, const char *command) 1024 | { 1025 | int ret; 1026 | unsigned char result; 1027 | 1028 | cmd = find_cmd_by_name(command); 1029 | if (!cmd) { 1030 | fprintf(stderr, "unknown command: %s\n", command); 1031 | return -1; 1032 | } 1033 | 1034 | ret = cc2530_do_cmd(cmd, NULL, &result); 1035 | if (ret) 1036 | return -1; 1037 | 1038 | printf("result: %02x\n", result); 1039 | 1040 | return 0; 1041 | } 1042 | 1043 | 1044 | static void usage(void) 1045 | { 1046 | printf("Usage: cc2530prog [options]\n" 1047 | "\t-v: verbose\n" 1048 | "\t-i: identify device\n" 1049 | "\t-P: show progress\n" 1050 | "\t-f: firmware file\n" 1051 | "\t-r: perform readback\n" 1052 | "\t-c: single command to send\n" 1053 | "\t-l: list available commands\n"); 1054 | exit(-1); 1055 | } 1056 | 1057 | int main(int argc, char **argv) 1058 | { 1059 | int opt, ret = 0; 1060 | const char *firmware = NULL; 1061 | unsigned do_readback = 0; 1062 | unsigned do_list = 0; 1063 | unsigned do_identify = 0; 1064 | char *command = NULL; 1065 | struct cc2530_cmd *cmd = NULL; 1066 | int f; 1067 | off_t fwsize; 1068 | struct stat buf; 1069 | int flash_size = 0; 1070 | unsigned int retry_cnt = 3; 1071 | 1072 | while ((opt = getopt(argc, argv, "f:rlc:ivP")) > 0) { 1073 | switch (opt) { 1074 | case 'f': 1075 | firmware = optarg; 1076 | break; 1077 | case 'r': 1078 | do_readback = 1; 1079 | break; 1080 | case 'l': 1081 | do_list = 1; 1082 | break; 1083 | case 'c': 1084 | command = optarg; 1085 | break; 1086 | case 'i': 1087 | do_identify = 1; 1088 | verbose = 1; 1089 | break; 1090 | case 'v': 1091 | verbose = 1; 1092 | break; 1093 | case 'P': 1094 | progress = 1; 1095 | break; 1096 | default: 1097 | break; 1098 | } 1099 | } 1100 | 1101 | argc -= optind; 1102 | argc += optind; 1103 | 1104 | if (argc < 2) 1105 | usage(); 1106 | 1107 | if (cc2530_gpio_init()) { 1108 | fprintf(stderr, "failed to initialize GPIOs\n"); 1109 | return -1; 1110 | } 1111 | 1112 | if (!debug_enabled) 1113 | cc2530_enter_debug(); 1114 | 1115 | if (do_identify) { 1116 | ret = cc2530_chip_identify(cmd, &flash_size); 1117 | if (ret) 1118 | fprintf(stderr, "failed to identify chip\n"); 1119 | 1120 | goto out; 1121 | } 1122 | 1123 | if (command) { 1124 | cc2530_oneshot_command(cmd, command); 1125 | goto out; 1126 | } 1127 | 1128 | if (do_list) { 1129 | cc2530_show_command_list(); 1130 | goto out; 1131 | } 1132 | 1133 | if (stat(firmware, &buf) < 0) { 1134 | perror("stat"); 1135 | goto out; 1136 | } 1137 | 1138 | if (!S_ISREG(buf.st_mode)) { 1139 | fprintf(stderr, "%s is not a regular file\n", firmware); 1140 | goto out; 1141 | } 1142 | 1143 | fwsize = buf.st_size; 1144 | 1145 | while (retry_cnt--) { 1146 | ret = cc2530_chip_identify(cmd, &flash_size); 1147 | if (ret) { 1148 | fprintf(stderr, "failed to identify chip\n"); 1149 | continue; 1150 | } 1151 | 1152 | break; 1153 | } 1154 | 1155 | if (!retry_cnt) { 1156 | fprintf(stderr, "timeout identifying the chip\n"); 1157 | goto out; 1158 | } 1159 | 1160 | if (fwsize > flash_size) { 1161 | fprintf(stderr, "firmware file too big: %ld (max: %d)\n", 1162 | fwsize, flash_size); 1163 | goto out; 1164 | } 1165 | 1166 | f = open(firmware, O_RDONLY); 1167 | if (!f) { 1168 | fprintf(stderr, "cannot open firmware: %s\n", firmware); 1169 | goto out; 1170 | } 1171 | 1172 | if (verbose) 1173 | printf("Using firmware file: %s (%ld bytes)\n", firmware, fwsize); 1174 | 1175 | fwdata = malloc(fwsize); 1176 | if (!fwdata) { 1177 | perror("malloc"); 1178 | ret = -1; 1179 | goto out; 1180 | } 1181 | 1182 | if (read(f, fwdata, fwsize) < 0) { 1183 | fprintf(stderr, "premature end of read\n"); 1184 | goto out_free; 1185 | } 1186 | 1187 | if (cc2530_do_program(cmd, fwsize, do_readback)) { 1188 | fprintf(stderr, "failed to program chip\n"); 1189 | ret = -1; 1190 | } 1191 | 1192 | out_free: 1193 | free(fwdata); 1194 | close(f); 1195 | out: 1196 | cc2530_leave_debug(); 1197 | cc2530_gpio_deinit(); 1198 | return ret; 1199 | } 1200 | -------------------------------------------------------------------------------- /gpio-sysfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux GPIO backend using sysfs 3 | * 4 | * Copyright (C) 2010, Florian Fainelli 5 | * 6 | * This file is part of "cc2530prog", this file is distributed under 7 | * a 2-clause BSD license, see LICENSE for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "gpio.h" 17 | 18 | #define SYSFS_GPIO "/sys/class/gpio" 19 | 20 | int read_file(const char *path, char *str, size_t size) 21 | { 22 | int fd; 23 | int ret; 24 | 25 | fd = open(path, O_RDONLY); 26 | if (fd < 0) { 27 | perror(path); 28 | return -1; 29 | } 30 | 31 | ret = read(fd, str, size - 1); 32 | if (ret < 0) { 33 | perror("read"); 34 | close(fd); 35 | return -1; 36 | } 37 | 38 | close(fd); 39 | str[ret] = '\0'; 40 | 41 | return 0; 42 | } 43 | 44 | int write_file(const char *path, const char *str) 45 | { 46 | int fd; 47 | int ret; 48 | 49 | fd = open(path, O_WRONLY); 50 | if (fd < 0) { 51 | perror(path); 52 | return -1; 53 | } 54 | 55 | ret = write(fd, str, strlen(str)); 56 | if (ret < 0) { 57 | if (errno == EBUSY) 58 | ret = 0; 59 | else 60 | perror("write"); 61 | } 62 | 63 | close(fd); 64 | 65 | return ret < 0 ? -1 : 0; 66 | } 67 | 68 | 69 | int 70 | gpio_export(int n) 71 | { 72 | char buf[16]; 73 | 74 | snprintf(buf, sizeof (buf), "%d", n); 75 | 76 | return write_file(SYSFS_GPIO "/export", buf); 77 | } 78 | 79 | int gpio_unexport(int n) 80 | { 81 | char buf[16]; 82 | 83 | snprintf(buf, sizeof(buf), "%d", n); 84 | 85 | return write_file(SYSFS_GPIO "/unexport", buf); 86 | } 87 | 88 | int 89 | gpio_set_direction(int n, enum gpio_direction direction) 90 | { 91 | static const char *str[] = { 92 | [GPIO_DIRECTION_IN] = "in", 93 | [GPIO_DIRECTION_OUT] = "out", 94 | [GPIO_DIRECTION_HIGH] = "high", 95 | }; 96 | char path[128]; 97 | 98 | snprintf(path, sizeof (path), SYSFS_GPIO "/gpio%d/direction", n); 99 | 100 | return write_file(path, str[direction]); 101 | } 102 | 103 | int 104 | gpio_get_value(int n, bool *value) 105 | { 106 | char buf[128]; 107 | 108 | snprintf(buf, sizeof (buf), SYSFS_GPIO "/gpio%d/value", n); 109 | 110 | if (read_file(buf, buf, sizeof (buf)) < 0) 111 | return -1; 112 | 113 | *value = (*buf != '0'); 114 | 115 | return 0; 116 | } 117 | 118 | int 119 | gpio_set_value(int n, bool value) 120 | { 121 | char path[128]; 122 | 123 | snprintf(path, sizeof (path), SYSFS_GPIO "/gpio%d/value", n); 124 | 125 | return write_file(path, value ? "1" : "0"); 126 | } 127 | -------------------------------------------------------------------------------- /gpio.h: -------------------------------------------------------------------------------- 1 | #ifndef __CC2530PROG_GPIO_H 2 | #define __CC2530PROG_GPIO_H 3 | 4 | #ifndef GPIO_BACKEND 5 | #error "unknown GPIO backend" 6 | #endif 7 | 8 | #include 9 | 10 | /* Reset polarity is active low */ 11 | #define RST_GPIO 0 12 | #define RST_GPIO_POL ! /* Active low polarity */ 13 | #define CCLK_GPIO 1 14 | #define DATA_GPIO 2 15 | 16 | /* 17 | * gpio sysfs helpers 18 | */ 19 | enum gpio_direction { 20 | GPIO_DIRECTION_IN, 21 | GPIO_DIRECTION_OUT, 22 | GPIO_DIRECTION_HIGH, 23 | }; 24 | 25 | int gpio_export(int n); 26 | int gpio_unexport(int n); 27 | int gpio_set_direction(int n, enum gpio_direction direction); 28 | int gpio_get_value(int n, bool *value); 29 | int gpio_set_value(int n, bool value); 30 | 31 | #endif /* __CC2530PROG_GPIO_H */ 32 | --------------------------------------------------------------------------------