nmc-utils  0.1.1
easynmc-core.c
Go to the documentation of this file.
1 /*
2  * libEasyNMC DSP communication library.
3  * Copyright (C) 2014 RC "Module"
4  * Written by Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * ut WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  *
20  */
21 
22 #define _GNU_SOURCE
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <sys/ioctl.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <getopt.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <alloca.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <libelf.h>
36 #include <gelf.h>
37 #include <easynmc.h>
38 #include <errno.h>
39 
40 
43 
44 #define dbg(fmt, ...) if (g_libeasynmc_debug) { \
45  fprintf(stderr, "libeasynmc: " fmt, ##__VA_ARGS__); \
46  }
47 
48 #define err(fmt, ...) if (g_libeasynmc_errors) { \
49  fprintf(stderr, "libeasynmc: " fmt, ##__VA_ARGS__); \
50  }
51 
52 
53 static uint32_t supported_startupcodes[] = {
54  0x20140715,
55  0x20141219,
56 };
57 
58 static void easynmc_warn_unsupported(uint32_t codever)
59 {
60  if (codever < 0x20141128) {
61  fprintf(stderr, "Warning: IPL version in use is outdated!\n");
62  fprintf(stderr, "Warning: This IPL does not support easynmc_appdata_*()!\n");
63  fprintf(stderr, "Warning: This IPL does not support EASYNMC_STATE_KILLABLE!\n");
64  }
65 }
66 
81 int easynmc_startupcode_is_compatible(uint32_t codever)
82 {
83  int i;
84  for (i=0; i<ARRAY_SIZE(supported_startupcodes); i++) {
85  if (supported_startupcodes[i] == codever)
86  return 1;
87  }
88  return 0;
89 }
90 
91 
99 {
100  struct nmc_core_stats stats;
101  int ret;
102  ret = ioctl(h->iofd, IOCTL_NMC3_GET_STATS, &stats);
103  if (ret != 0) {
104  perror("ioctl");
105  return EASYNMC_CORE_INVALID;
106  }
107 
108  if (!stats.started)
109  return EASYNMC_CORE_COLD;
110 
111  uint32_t codever = h->imem32[NMC_REG_CODEVERSION];
112 
113  if (!easynmc_startupcode_is_compatible(codever))
114  return EASYNMC_CORE_INVALID;
115 
116  /* Warn the user of any unsupported features */
117  easynmc_warn_unsupported(codever);
118 
119  uint32_t status = h->imem32[NMC_REG_CORE_STATUS];
120  if (status > EASYNMC_CORE_INVALID)
121  return EASYNMC_CORE_INVALID;
122  return status;
123 }
124 
125 static const char* statuses[] = {
126  "cold",
127  "idle",
128  "running",
129  "paused",
130  "killable",
131  "invalid",
132 };
133 
141 const char* easynmc_state_name(enum easynmc_core_state state)
142 {
143  if (state > EASYNMC_CORE_INVALID)
144  state = EASYNMC_CORE_INVALID;
145  return statuses[state];
146 }
147 
155 int easynmc_get_core_name(struct easynmc_handle *h, char* str)
156 {
157  return ioctl(h->iofd, IOCTL_NMC3_GET_NAME, str);
158 }
159 
167 int easynmc_get_core_type(struct easynmc_handle *h, char* str)
168 {
169  return ioctl(h->iofd, IOCTL_NMC3_GET_TYPE, str);
170 }
171 
179 int easynmc_send_irq(struct easynmc_handle *h, enum nmc_irq irq)
180 {
181  return ioctl(h->iofd, IOCTL_NMC3_SEND_IRQ, &irq);
182 }
183 
194 {
195  return ioctl(h->iofd, IOCTL_NMC3_RESET_STATS, NULL);
196 }
197 
198 
205 {
206  ioctl(h->iofd, IOCTL_NMC3_RESET, NULL);
207 }
208 
209 
210 static int str2nmc(uint32_t *dst, char* src, int len)
211 {
212  int ret = len;
213  while(len--)
214  *dst++ = *src++;
215  return ret;
216 }
217 
218 /* FixMe: Take $(PREFIX) into account */
219 
220 char *iplpaths[] = {
221  "/usr/share/easynmc-" LIBEASYNMC_VERSION "/ipl/ipl-%s%s.abs",
222  "/usr/local/share/easynmc-" LIBEASYNMC_VERSION "/ipl/ipl-%s%s.abs",
223  "./ipl-%s.abs"
224 };
225 
235 char *easynmc_get_default_ipl(char* name, int debug)
236 {
237  int i;
238  char *tmp;
239 
240  for (i=0; i<ARRAY_SIZE(iplpaths); i++) {
241  asprintf(&tmp, iplpaths[i], name, debug ? "-debug" : "");
242  dbg("trying: %s\n", tmp)
243  if (0 == access(tmp, R_OK))
244  return tmp;
245  free(tmp);
246  }
247  return NULL;
248 
249 }
250 
251 
252 static int find_unused(struct easynmc_handle *h, void *udata)
253 {
254  struct easynmc_handle **dest = udata;
257  *dest = h;
259  }
260  return 0;
261 }
262 static int find_killable(struct easynmc_handle *h, void *udata)
263 {
264  struct easynmc_handle **dest = udata;
266  *dest = h;
268  }
269  return 0;
270 }
271 
284 struct easynmc_handle *easynmc_open_noboot(int coreid, int exclusive)
285 {
286  char path[1024];
287  int ret;
288 
289  struct easynmc_handle *h = NULL;
290 
291  if (coreid == EASYNMC_CORE_ANY) {
292  easynmc_for_each_core(find_unused, exclusive, &h);
293  if (!h)
294  easynmc_for_each_core(find_killable, exclusive, &h);
295  return h;
296  }
297 
298  dbg("opening core %d\n", coreid);
299 
300  h = malloc(sizeof(struct easynmc_handle));
301  if (!h)
302  return NULL;
303  h->persistent = 0;
304  /* let's open core mem, io, and do the mmap */
305  h->id = coreid;
306 
307  h->appid = NULL;
308 
309  h->sfilters = NULL;
310 
311  sprintf(path, "/dev/nmc%dio", coreid);
312  h->iofd = open(path, O_RDWR);
313  if (!h->iofd) {
314  err("Couldn't open NMC IO device\n");
315  goto errfreenmc;
316  }
317 
318  sprintf(path, "/dev/nmc%d", coreid);
319  h->memfd = open(path, O_RDWR);
320  if (!h->memfd) {
321  err("Couldn't open NMC MEM device\n");
322  goto errcloseiofd;
323  }
324 
325  if (exclusive && (flock(h->memfd, LOCK_EX | LOCK_NB) != 0)) /* Locking failed */
326  goto errcloseiofd;
327 
328  ret = ioctl(h->iofd, IOCTL_NMC3_GET_IMEMSZ, &h->imem_size);
329  if (ret!=0) {
330  err("Couldn't get NMC internal memory size\n");
331  goto errclosememfd;
332  }
333 
334  h->imem = mmap(NULL, h->imem_size, PROT_READ | PROT_WRITE, MAP_SHARED, h->memfd, 0);
335  if (h->imem == MAP_FAILED) {
336  err("Couldn't MMAP internal memory\n");
337  goto errclosememfd;
338  }
339 
340  h->imem32 = (uint32_t *) h->imem;
341 
342  dbg("Opened core %d iofd %d memfd %d. mmap imem %u bytes @ 0x%lx\n",
343  coreid, h->iofd, h->memfd, h->imem_size, (unsigned long) h->imem);
344 
346 
347  return h;
348 
349 errclosememfd:
350  close(h->memfd);
351 errcloseiofd:
352  close(h->iofd);
353 errfreenmc:
354  free(h);
355  err("Device was: %s\n", path);
356  return NULL;
357 }
358 
373 int easynmc_boot_core(struct easynmc_handle *h, int debug)
374 {
375  int ret;
376  uint32_t ep;
377  const char* startupfile = getenv("NMC_STARTUPCODE");
378  char name[64];
379 
381  err("core already started, will not load ipl again\n");
382  return 1;
383  }
384 
385  ret = easynmc_get_core_name(h, name);
386  if (ret)
387  return ret;
388 
389  if (!startupfile)
390  startupfile = easynmc_get_default_ipl(name, debug);
391 
392  if (!startupfile) {
393  err("Didn't find startup code file. Did you install one?\n");
394  err("HINT: You can set env variable NMC_STARTUPCODE\n");
395  return -1;
396  }
397 
398  dbg("Booting core using: %s file\n", startupfile);
399 
400  ret = easynmc_load_abs(h, startupfile, &ep, 0);
401  if (ret!=0)
402  return ret;
403 
404  if (ep != 0) {
405  err("ERROR: Startup code entry point must be 0x0!\n");
406  err("ERROR: We have 0x%x! Cowardly refusing to go further\n", ep);
407  return 1;
408  }
409 
411 
412  /* We want an HPINT when startup code is running */
414 
415  struct easynmc_token *tok = easynmc_token_new(h, EASYNMC_EVT_HP | EASYNMC_EVT_LP);
416  if (!tok)
417  return 1;
418 
419  ret = easynmc_send_irq(h, NMC_IRQ_NMI);
420  if (ret != 0)
421  return ret;
422  int evt;
423 
424  evt = easynmc_token_wait(tok, 5000);
425  dbg("Got evt %d\n", evt);
426  switch(evt)
427  {
428  case EASYNMC_EVT_TIMEOUT:
429  err("Timeout waiting for initcode to start\n");
431  err("But init code reports being ready. \n");
432  err("Did you mix up HP and LP interrupts in DeviceTree?\n");
433  return 0; /* Okay, nevertheless */
434  }
435  return 1;
436  break;
437  case EASYNMC_EVT_HP:
438  dbg("Initial code loaded & ready \n");
439  return 0;
440  break;
441  default:
442  err("Got event %d (%s) instead of EVT_HP\n", evt, easynmc_evt_name(evt));
443  err("Did you mix up HP and LP interrupts in DeviceTree?\n");
444  return 1;
445  break;
446  }
447 
448  /* TODO: wait for loader, check version */
449  return 0;
450 }
451 
521 int easynmc_set_args(struct easynmc_handle *h, char* self, int argc, char **argv)
522 {
523  int i;
524  int len;
525  int needspace = argc + strlen(self) + 1;
526 
527  if (!h->argoffset) {
528  err("No argument offset found for this handle\n");
529  return -1;
530  }
531 
532  for (i=0; i<argc; i++)
533  needspace += strlen(argv[i]) + 1;
534 
535  if (needspace > h->argdatalen) {
536  err("Arguments exceed available space.\n");
537  return -2;
538  }
539 
540  /* Let's make some black magic */
541 
542  h->imem32[h->argoffset] = argc + 1 ;
543  h->imem32[h->argoffset+1] = h->argoffset + 2;
544 
545  uint32_t *ptroff = &h->imem32[h->argoffset + 2];
546  uint32_t *dataoff = &h->imem32[h->argoffset + 2 + argc + 1];
547 
548  *ptroff = dataoff - h->imem32;
549  len = str2nmc(dataoff, self, strlen(self)+1);
550  dataoff += len;
551 
552  for (i=0; i<argc; i++) {
553  ptroff[i+1] = dataoff - h->imem32;
554  len = str2nmc(dataoff, argv[i], strlen(argv[i])+1);
555  dataoff += len;
556  }
557 
558  return 0;
559 }
560 
561 
582 int easynmc_load_abs(struct easynmc_handle *h, const char *path, uint32_t* ep, int flags)
583 {
584  int i;
585  char *id;
586  GElf_Ehdr ehdr;
587  GElf_Shdr shdr;
588  Elf *elf;
589  int fd;
590  Elf_Scn *scn = NULL;
591 
592  const char *state = easynmc_state_name(easynmc_core_state(h));
593 
594  if (!(flags & ABSLOAD_FLAG_FORCE))
598  {
599  err("ERROR: Attempt to load abs when core is '%s'\n", state);
600  err("ERROR: Will not do that unless --force'd\n");
601  return 1;
602  }
603 
604  FILE* rfd = fopen(path, "rb");
605  if (NULL==rfd) {
606  perror("fopen");
607  return -1;
608  }
609  if ((fd = open(path, O_RDONLY)) == -1) {
610  perror("open");
611  goto errfclose;
612  }
613 
614 
615  if(elf_version(EV_CURRENT) == EV_NONE)
616  {
617  err("WARNING: Elf Library is out of date!\n");
618  }
619 
620  if ((elf = elf_begin(fd, ELF_C_READ , NULL)) == NULL) {
621  err("elf_begin() failed: %s.",
622  elf_errmsg(-1));
623  goto errclose;
624  }
625 
626  if (gelf_getehdr(elf, &ehdr) == NULL) {
627  err( "getehdr() failed: %s.",
628  elf_errmsg(-1));
629  goto errclose;
630  }
631 
632  if ((i = gelf_getclass(elf)) == ELFCLASSNONE) {
633  err( "getclass() failed: %s.",
634  elf_errmsg(-1));
635  goto errclose;
636  }
637 
638  dbg("ELF Looks like a %d-bit ELF object\n",
639  i == ELFCLASS32 ? 32 : 64);
640 
641  if ((id = elf_getident(elf, NULL)) == NULL) {
642  err( "getident() failed: %s.",
643  elf_errmsg(-1));
644  goto errclose;
645  }
646 
647  dbg("ELF Machine id: 0x%x\n", ehdr.e_machine);
648 
649  *ep = ehdr.e_entry;
650 
651  elf = elf_begin(fd, ELF_C_READ , NULL);
652 
653  h->argoffset = 0;
654 
655  while((scn = elf_nextscn(elf, scn)) != 0)
656  {
657  char* why_skip = NULL;
658  gelf_getshdr(scn, &shdr);
659  char* name = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
660  int addr = shdr.sh_addr << 2;
661 
662  if (shdr.sh_size == 0) /* Skip empty sections */
663  why_skip = "(empty section)";
664 
665  if (0==strcmp(name,".memBankMap")) {
666  why_skip = "(not needed)";
667  }
668 
669  if (0==strcmp(name,".shstrtab")) {
670  why_skip = "(not needed)";
671  }
672 
673  if (shdr.sh_type == SHT_NOBITS) {
674  why_skip = "(nobits)";
675  memset(&h->imem[addr], 0x0, shdr.sh_size);
676  }
677 
678  if (0==strcmp(name,".bss")) {
679  why_skip = "(cleansing)";
680  memset(&h->imem[addr], 0x0, shdr.sh_size);
681  }
682 
683 
684  dbg("%s section %s %s %ld bytes @ 0x%x\n",
685  why_skip ? "Skipping" : "Uploading",
686  name,
687  why_skip ? why_skip : "",
688  (unsigned long) shdr.sh_size,
689  addr );
690 
691 
692  size_t ret;
693 
694  if (!why_skip) {
695  ret = fseek(rfd, shdr.sh_offset, SEEK_SET);
696  if (ret !=0 ) {
697  err("Seek failed, bad elf\n");
698  goto errclose;
699  }
700  dbg("read to %d size %ld\n", addr, (unsigned long) shdr.sh_size);
701  ret = fread(&h->imem[addr], 1, shdr.sh_size, rfd);
702  if (ret != shdr.sh_size ) {
703  err("Ooops, failed to read all data: want %ld got %zd\n",
704  (unsigned long) shdr.sh_size, ret);
705  perror("fread");
706  goto errclose;
707  }
708  }
709 
710  /* Now call the section filter chain */
711  struct easynmc_section_filter *f = h->sfilters;
712  int handled = 0;
713 
714  while (!handled && f) {
715  dbg("Aplying section filter %s\n", f->name);
716  handled = f->handle_section(h, name, rfd, shdr);
717  f = f->next;
718  }
719 
720  }
721  close(fd);
722  fclose(rfd);
723  dbg("Elvish loading done!\n");
724  return 0;
725 
726 errclose:
727  close(fd);
728 
729 errfclose:
730  fclose(rfd);
731  return -1;
732 }
733 
734 
743 int easynmc_start_app(struct easynmc_handle *h, uint32_t entry)
744 {
746  if (s != EASYNMC_CORE_IDLE) {
747  err("Core is in state %s, must be idle\n", easynmc_state_name(s));
748  return 1;
749  }
750 
751  h->imem32[NMC_REG_PROG_ENTRY] = entry;
752  h->imem32[NMC_REG_CORE_START] = 1;
753  return 0;
754 }
755 
756 
764 {
765  return h->imem32[NMC_REG_PROG_RETURN];
766 }
767 
781 {
782  int ret;
783  int state = easynmc_core_state(h);
784 
785  if ((state != EASYNMC_CORE_RUNNING) &&
786  (state != EASYNMC_CORE_KILLABLE)) {
787  err("App not running, won't stop it\n");
788  return 1;
789  }
790 
791  ret = easynmc_send_irq(h, NMC_IRQ_NMI);
792  if (ret!=0) {
793  perror("send-irq");
794  return 1;
795  }
796 
797  int timeout = 100;
798  while ((easynmc_core_state(h) != EASYNMC_CORE_IDLE) && --timeout) {
799  usleep(1000);
800  }
801 
802  dbg("timeout remaining %d\n", timeout);
803 
804  return timeout ? 0 : 1;
805 }
806 
817 {
818  if (!h->sfilters) {
819  h->sfilters = f;
820  f->next = NULL;
821  return;
822  }
823 
824  struct easynmc_section_filter *tail = h->sfilters;
825  while (tail->next != NULL)
826  tail = tail->next;
827  tail->next = f;
828  f->next = NULL;
829 }
830 
831 
832 
849 struct easynmc_handle *easynmc_open(int coreid)
850 {
851  int ret =0;
852  struct easynmc_handle *h = NULL;
853 
854 
855 
856  h = easynmc_open_noboot(coreid, 1);
857 
858  if (!h)
859  return NULL;
860 
862 
863  if (s == EASYNMC_CORE_COLD)
864  ret = easynmc_boot_core(h, 0);
865 
866  if (s == EASYNMC_CORE_KILLABLE)
867  ret = easynmc_stop_app(h);
868 
870 
871  if (ret != 0)
872  goto errfreeh;
873 
874  if (ret != 0)
875  goto errfreeh;
876 
877  return h;
878 
879 errfreeh:
880  easynmc_close(h);
881  return NULL;
882 }
883 
884 
903 int easynmc_for_each_core(int (*core_cb)(struct easynmc_handle *h, void *udata), int exclusive, void *udata)
904 {
905  FILE *fd = fopen("/proc/nmc", "r");
906  if (!fd) {
907  err("/proc/nmc open failed\n");
908  return -EIO;
909  }
910  char tmp[512];
911  int ret = 0;
912  while (fgets(tmp, 512, fd)) {
913  int coreid;
914  struct easynmc_handle *h;
915  if (1 == sscanf(tmp, "/dev/nmc%d", &coreid)) {
916  h = easynmc_open_noboot(coreid, exclusive);
917  if (h) {
918  ret++;
919  int result = core_cb(h, udata);
920  if (result & EASYNMC_ITERATE_STOP)
921  break;
922  if (!(result & EASYNMC_ITERATE_NOCLOSE))
923  easynmc_close(h);
924  }
925  }
926  }
927  return ret;
928 }
929 
939 void easynmc_close(struct easynmc_handle *hndl)
940 {
941  /* Mark the application as killable.
942  N.B. There's no race condition here, since if the app exit()s after
943  the check, but before updating the NMC_REG_CORE_STATUS, init code
944  will overwrite it.
945  If persistance is not enabled - app will be killed by kernel on
946  close.
947  */
948 
949  dbg("close core id %d \n", hndl->id);
950  if ((hndl->persistent) && (easynmc_core_state(hndl) == EASYNMC_CORE_RUNNING))
952 
953  /* Unlock */
954  flock(hndl->memfd, LOCK_UN);
955  /* Cleanup */
956  close(hndl->iofd);
957  close(hndl->memfd);
958  munmap(hndl->imem, hndl->imem_size);
959  free(hndl);
960 }
961 
962 
987 {
988  int ret;
989  ret = ioctl(t->h->iofd, IOCTL_NMC3_RESET_TOKEN, &t->tok);
990  if (ret != 0)
991  perror("ioctl");
992  dbg("New token id %d\n", t->tok.id);
993  return ret;
994 }
995 
1005 struct easynmc_token *easynmc_token_new(struct easynmc_handle *h, uint32_t events)
1006 {
1007  struct easynmc_token *t = calloc(1, sizeof(struct easynmc_token));
1008  if (!t)
1009  return NULL;
1010  t->h = h;
1011 
1012  t->tok.events_enabled = events;
1013 
1014  if ( 0 != easynmc_token_clear(t) ) {
1015  free(t);
1016  return NULL;
1017  }
1018 
1019  return t;
1020 }
1021 
1028 const char* easynmc_evt_name(int evt)
1029 {
1030  switch(evt) {
1031  case EASYNMC_EVT_LP:
1032  return "LP";
1033  case EASYNMC_EVT_HP:
1034  return "HP";
1035  case EASYNMC_EVT_NMI:
1036  return "NMI";
1037  case EASYNMC_EVT_ERROR:
1038  return "ERROR";
1039  case EASYNMC_EVT_CANCELLED:
1040  return "CANCELLED";
1041  case EASYNMC_EVT_TIMEOUT:
1042  return "TIMEOUT";
1043  default:
1044  return "WTF!?";
1045  }
1046 }
1047 
1055 int easynmc_token_wait(struct easynmc_token *t, uint32_t timeout) {
1056  int ret;
1057  t->tok.timeout = timeout;
1058  ret = ioctl(t->h->iofd, IOCTL_NMC3_WAIT_ON_TOKEN, &t->tok);
1059  if (ret != 0) {
1060  err("ioctl returned %d\n", ret);
1061  perror("ioctl");
1062  return EASYNMC_EVT_ERROR;
1063  }
1064  return t->tok.event;
1065 }
1066 
1098 {
1099  return ioctl(h->iofd, IOCTL_NMC3_POLLMARK, NULL);
1100 }
1101 
1102 
1147 #ifndef __DOXYGEN__
1148 struct easynmc_connect_info {
1149  struct easynmc_handle *desth;
1150  const char* appid;
1151 };
1152 #endif
1153 
1154 static int connect_core_cb(struct easynmc_handle *h, void *udata)
1155 {
1156  struct easynmc_connect_info *i = udata;
1158  (strcmp(easynmc_appid_get(h), i->appid) == 0)) {
1159 
1160  if (flock(h->memfd, LOCK_EX | LOCK_NB) != 0)
1161  return 0;
1162 
1163  i->desth = h;
1165  }
1166  return 0;
1167 }
1168 
1190 {
1191  struct easynmc_connect_info i;
1192  i.desth = NULL;
1193  i.appid = appid;
1194  easynmc_for_each_core(connect_core_cb, 1, &i);
1195  if (i.desth)
1196  i.desth->persistent = 1;
1197  return i.desth;
1198 }
1199 
1200 #ifndef __DOXYGEN__
1201 struct easynmc_appdata {
1202  char appid[EASYNMC_APPID_LEN];
1203  char userdata[];
1204 };
1205 #endif
1206 
1207 static inline size_t raw_appdata_size(struct easynmc_handle *h)
1208 {
1209  return h->imem32[NMC_REG_APPDATA_SIZE];
1210 }
1211 
1218 {
1219  if (!raw_appdata_size(h))
1220  return 0;
1221  /* The actual size, minus extra information data */
1222  return raw_appdata_size(h) - sizeof(struct easynmc_appdata);
1223 }
1224 
1225 static int kernel_appdata_get(struct easynmc_handle *h, struct nmc_ioctl_buffer *buf)
1226 {
1227  if (!raw_appdata_size(h))
1228  return -ENODATA;
1229 
1230  int ret = ioctl(h->iofd, IOCTL_NMC3_GET_APPDATA, buf);
1231  dbg("kernel appdata get: %d len %zu\n", ret, buf->len);
1232  return ret;
1233 }
1234 
1235 static int kernel_appdata_set(struct easynmc_handle *h, struct nmc_ioctl_buffer *buf)
1236 {
1237  int ret = ioctl(h->iofd, IOCTL_NMC3_SET_APPDATA, buf);
1238 
1239  /* HACK: Is it a good idea to store appdata len here ? */
1240  if (ret == 0)
1241  h->imem32[NMC_REG_APPDATA_SIZE] = buf->len;
1242 
1243  dbg("kernel appdata set: %d len %zu\n", ret, buf->len);
1244  return ret;
1245 }
1246 
1247 
1257 int easynmc_appdata_set(struct easynmc_handle *h, void *data, size_t len)
1258 {
1259  size_t dlen = len + sizeof(struct easynmc_appdata);
1260  struct easynmc_appdata *adata = alloca(dlen);
1261  struct nmc_ioctl_buffer buf;
1262  buf.data = adata;
1263  buf.len = len;
1264  int ret;
1265 
1266  ret = kernel_appdata_get(h, &buf);
1267  if (ret == -ENODATA) {
1268  adata->appid[0]=0x0; /* NULL AppID */
1269  } else if (ret)
1270  return ret;
1271 
1272  memcpy(adata->userdata, data, len);
1273  return kernel_appdata_set(h, &buf);
1274 
1275 }
1276 
1286 size_t easynmc_appdata_get(struct easynmc_handle *h, void *data, size_t len)
1287 {
1288  int ret;
1289  size_t dlen = raw_appdata_size(h);
1290  if (!dlen)
1291  return 0; /* Appdata is invalid */
1292 
1293  size_t actual_length = raw_appdata_size(h) -
1294  sizeof(struct easynmc_appdata);
1295 
1296  struct easynmc_appdata *adata = alloca(dlen);
1297  struct nmc_ioctl_buffer buf;
1298  buf.data = adata;
1299  buf.len = dlen;
1300 
1301  ret = kernel_appdata_get(h, &buf);
1302  if (ret)
1303  return 0;
1304  size_t tocopy = (len < actual_length) ? len : actual_length;
1305  memcpy(data, adata->userdata, tocopy);
1306  return tocopy;
1307 }
1308 
1316 int easynmc_appid_set(struct easynmc_handle *h, char appid[])
1317 {
1318  int ret;
1319 
1320  if (strlen(appid) > EASYNMC_APPID_LEN)
1321  return -EIO;
1322 
1323  if (h->appid)
1324  free(h->appid);
1325  h->appid = strdup(appid);
1326 
1327  size_t rawsize = raw_appdata_size(h);
1328 
1329  if (!rawsize)
1330  rawsize = sizeof(struct easynmc_appdata);
1331 
1332  struct easynmc_appdata *adata = alloca(rawsize);
1333  struct nmc_ioctl_buffer buf;
1334  buf.data = adata;
1335  buf.len = rawsize;
1336 
1337  ret = kernel_appdata_get(h, &buf);
1338  if (ret && (ret != -ENODATA))
1339  return ret;
1340 
1341  buf.len = rawsize;
1342 
1343  strncpy(adata->appid, appid, EASYNMC_APPID_LEN);
1344  adata->appid[EASYNMC_APPID_LEN-1] = 0x0;
1345 
1346  return kernel_appdata_set(h, &buf);
1347 }
1348 
1357 const char *easynmc_appid_get(struct easynmc_handle *h)
1358 {
1359  int ret;
1360  if (h->appid)
1361  return (const char*) h->appid;
1362 
1363  size_t dlen = h->imem32[NMC_REG_APPDATA_SIZE];
1364  if (!dlen)
1365  return NULL; /* AppId is invalid */
1366 
1367  size_t sz = sizeof(struct easynmc_appdata);
1368  struct easynmc_appdata *adata = alloca(sz);
1369  struct nmc_ioctl_buffer buf;
1370  buf.data = adata;
1371  buf.len = sz;
1372  ret = kernel_appdata_get(h, &buf);
1373  if (ret)
1374  return NULL;
1375  h->appid = strdup(adata->appid);
1376  return h->appid;
1377 }
1378 
1394 {
1395  h->persistent = (status == EASYNMC_PERSIST_ENABLE) ? 1 : 0;
1396  int ret = ioctl(h->iofd, IOCTL_NMC3_NMI_ON_CLOSE, &status);
1397  if (ret != 0) {
1398  perror("ioctl");
1399  }
1400  return ret;
1401 }
1402