]> rtime.felk.cvut.cz Git - mf624-simulink.git/blob - mf624_SIMULINK.c
Use correct header file for alloca for Linux build.
[mf624-simulink.git] / mf624_SIMULINK.c
1 /*
2  * Common Humusoft MF624 card Simulink code for use with Linux UIO driver
3  *
4  * Copyright (C) 2011-2014 Rostislav Lisovy <lisovy@gmail.com>
5  * Copyright (C) 2013 Michal Kreč <krecmich@fel.cvut.cz>
6  * Copyright (C) 2013 Michal Sojka <sojkam1@fel.cvut.cz>
7  *
8  * Department of Control Engineering
9  * Faculty of Electrical Engineering
10  * Czech Technical University in Prague (CTU)
11  *
12  * The ERT Linux support code can be distributed in compliance
13  * with GNU General Public License (GPL) version 2 or later.
14  * Other licence can negotiated with CTU.
15  *
16  * Next exception is granted in addition to GPL.
17  * Instantiating or linking compiled version of this code
18  * to produce an application image/executable, does not
19  * by itself cause the resulting application image/executable
20  * to be covered by the GNU General Public License.
21  * This exception does not however invalidate any other reasons
22  * why the executable file might be covered by the GNU Public License.
23  * Publication of enhanced or derived S-function files is required
24  * although.
25  *
26  * Linux ERT code is available from
27  *    http://rtime.felk.cvut.cz/gitweb/ert_linux.git
28  * More CTU Linux target for Simulink components are available at
29  *    http://lintarget.sourceforge.net/
30  *
31  * sfuntmpl_basic.c by The MathWorks, Inc. has been used to accomplish
32  * required S-function structure.
33  */
34
35 #include <stdio.h>
36 #include <errno.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <dirent.h>
42 #include <sys/types.h>
43 #include <sys/mman.h>
44 #include <stdint.h> // uintX_t
45 #include <inttypes.h>
46 #include <unistd.h>
47 #include <alloca.h>
48 #include <stdlib.h>
49
50 #include "mf624_SIMULINK.h"
51
52 /****************************************************************/
53
54
55 /* Which uio device node to use */
56 #define UIO "uio0"
57
58 mf624_state_t* mfst=NULL;
59 unsigned mfst_refcnt = 0;
60
61 static int bar_mapping_fill(bar_mapping_t *barmap, const char *uio_dev, int map_nr)
62 {
63         FILE *file;
64         void *s;
65         int ssiz;
66         static size_t page_size;
67
68         page_size = sysconf(_SC_PAGESIZE);
69
70         ssiz = snprintf(NULL, 0, "/sys/class/uio/%s/maps/map%i/", uio_dev, map_nr);
71         if (ssiz < 0)
72                 return -1;
73         /* add reserve to store each size addr, name, offset, size */
74         ssiz += 6 + 1;
75         s = alloca(ssiz);
76
77         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/addr", uio_dev, map_nr);
78         file = fopen(s, "rb");
79         if (file == NULL)
80           return -1;
81         fscanf(file, "%"SCNiPTR, &barmap->phys_addr);
82         fclose(file);
83
84         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/offset", uio_dev, map_nr);
85         file = fopen(s, "rb");
86         if (file == NULL) {
87                 barmap->offset = barmap->phys_addr & (page_size - 1);
88         } else {
89                 fscanf(file, "%"SCNi32, &barmap->offset);
90                 fclose(file);
91         }
92
93         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/size", uio_dev, map_nr);
94         file = fopen(s, "rb");
95         if (file == NULL)
96           return -1;
97         fscanf(file, "%"SCNi32, &barmap->size);
98         fclose(file);
99
100         barmap->mmap_offset = page_size * map_nr + barmap->offset;
101
102         return 0;
103 }
104
105 static int bar_mapping_setup(bar_mapping_t *barmap, int device_fd)
106 {
107         static size_t page_mask = 0;
108         off_t mmap_start;
109         size_t mmap_size;
110
111         if (!page_mask)
112                  page_mask = sysconf(_SC_PAGESIZE) - 1;
113
114         mmap_start = barmap->mmap_offset & ~page_mask;
115         mmap_size = barmap->mmap_offset + barmap->size + page_mask - mmap_start;
116         mmap_size &= ~page_mask;
117
118         barmap->mmap_addr = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, mmap_start);
119         if (barmap->mmap_addr == MAP_FAILED) {
120                 return -1;
121         }
122
123         barmap->virt_addr = (uintptr_t)barmap->mmap_addr;
124         barmap->virt_addr += barmap->mmap_offset & page_mask;
125
126         return 0;
127 }
128
129 static int bar_mapping_destroy(bar_mapping_t *barmap)
130 {
131         off_t mmap_start;
132         size_t mmap_size;
133         size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
134
135         mmap_start = barmap->mmap_offset & ~page_mask;
136         mmap_size = barmap->mmap_offset + barmap->size + page_mask - mmap_start;
137         mmap_size &= ~page_mask;
138
139         return munmap(barmap->mmap_addr, mmap_size);
140 }
141
142 /****************************************************************/
143
144 #define BUFF_SMALL              32
145 #define BUFF_MID                256
146 #define min(a, b)               ((a) > (b) ? (b) : (a))
147
148
149
150
151
152 mf624_state_t mf624_state;
153
154 /* Print one byte as binary number */
155 static void print_8bin(int nr)
156 {
157         int i;
158         for (i = 7; i >= 0; i--) {
159                 printf("%d" , ((nr & (1 << i)) > 0));
160         }
161
162         printf("\n");
163 }
164
165
166
167 void DIO_write(mf624_state_t* mfst, int16_t val)
168 {
169         mf624_write16(val, MFST2REG(mfst, 2, DOUT_reg));
170 }
171
172 uint16_t DIO_read(mf624_state_t* mfst)
173 {
174         return mf624_read16(MFST2REG(mfst, 2, DIN_reg)) & 0xFF;
175 }
176
177 void DAC_enable(mf624_state_t* mfst)
178 {
179         // Setting DACEN and LDAC bits in GPIO register influences all DACs
180         mf624_write32((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg))
181                 | GPIOC_DACEN_mask) // enable output
182                 & ~GPIOC_LDAC_mask, // enable conversion
183                 MFST2REG(mfst, 0, GPIOC_reg));
184 }
185
186
187
188 int DAC_write(mf624_state_t* mfst, dac_channel_t channel, int val)
189 {
190         if (channel > sizeof(dac_channel2reg)/sizeof(*dac_channel2reg))
191                 return -1;
192
193         mf624_write16(val, MFST2REG(mfst, 2, dac_channel2reg[channel]));
194         return 0;
195 }
196
197 int ADC_enable(mf624_state_t* mfst, adc_channel_t channel)
198 {
199         mfst->ADC_enabled = 0;
200
201         if (channel > sizeof(adc_channel2reg)/sizeof(*adc_channel2reg))
202                 return -1;
203
204         mfst->ADC_enabled = (1 << channel);
205
206         mfst->ADC_enabled &= 0xFF;
207         mf624_write16(mfst->ADC_enabled, MFST2REG(mfst, 2, ADCTRL_reg));
208         //print_8bin(ADC_enabled);
209
210         return 0;
211 }
212
213 /* This function blocks until conversion is finished */
214 double ADC_read(mf624_state_t* mfst, adc_channel_t channel)
215 {
216         volatile int i;
217         int result;
218
219         // Activate trigger to start conversion
220         mf624_read16(MFST2REG(mfst, 2, ADSTART_reg));
221
222         // Check if conversion has finished
223         while((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg)) & GPIOC_EOLC_mask)) {
224                 for (i = 0; i < 1000; i++) {} // small wait
225         }
226
227         ADC_enable(mfst, channel);
228         result = mf624_read16(MFST2REG(mfst, 2, ADDATA0_reg));
229
230         return 10.0 * ((int16_t) (result << 2)) / (double) 0x8000;
231 }
232
233 extern uint32_t IRC_mode_change(mf624_state_t* mfst, uint32_t change_mask, uint32_t change_val)
234 {
235         /* This sequence should be protected by mutex to protect changes in multirate systems */
236         mfst->IRC_mode = (mfst->IRC_mode & ~change_mask) | (change_val & change_mask);
237         mf624_write32(mfst->IRC_mode, MFST2REG(mfst, 4, IRCCTRL_reg));
238         /*printf("change_mask 0x%08x, change_val 0x%08x\n", change_mask, change_val);*/
239         /*printf("IRC mode set to %08lx\n", mfst->IRC_mode);*/
240         return mfst->IRC_mode;
241 }
242
243
244 static int open_device(char* path) {
245         int device_fd;
246
247         device_fd = open(path, O_RDWR | O_SYNC);
248         if (device_fd == -1) {
249                 perror(path);
250                 return -1;
251         }
252
253         return device_fd;
254 }
255
256 static void wait_for_interrupts(int device_fd)
257 {
258         read(device_fd, NULL, 1);
259 }
260
261 static int disable_interrupts(int device_fd)
262 {
263         uint32_t control_value = 0;
264         int status;
265
266         status = write(device_fd, &control_value, sizeof(uint32_t));
267         if (status == -1) {
268                 perror("write()");
269                 return -1;
270         }
271
272         return status;
273 }
274
275 static int enable_interrupts(int device_fd)
276 {
277         uint32_t control_value = 1;
278         int status;
279
280         status = write(device_fd, &control_value, sizeof(uint32_t));
281         if (status == -1) {
282                 perror("write()");
283                 return -1;
284         }
285
286         return status;
287 }
288
289 static void list_available_mem_regions(char* device)
290 {
291         int status;
292         char path[] = "/sys/class/uio/";
293         char subdir[] = "/maps/";
294         char directory[BUFF_MID];
295         memset(directory, '\0', BUFF_MID);
296
297         DIR *dip;
298         struct dirent *dit;
299
300         strncat(directory, path, strlen(path));
301         strncat(directory, device, min(strlen(device), 8));
302         strncat(directory, subdir, strlen(subdir));
303
304         dip = opendir(directory);
305         if (dip == NULL) {
306                 perror("opendir");
307                 return;
308         }
309
310         while ((dit = readdir(dip)) != NULL) {
311                 if (strcmp(dit->d_name, ".") && strcmp(dit->d_name, "..")) {
312                         printf(" %s\n", dit->d_name);
313                 }
314         }
315
316         status = closedir(dip);
317         if (status == -1) {
318                 perror("closedir()");
319                 return;
320         }
321
322 }
323
324
325 static void list_available_io_ports(char *device)
326 {
327         int status;
328         char path[] = "/sys/class/uio/";
329         char subdir[] = "/portio/";
330         char directory[BUFF_MID];
331         memset(directory, '\0', BUFF_MID);
332
333         DIR *dip;
334         struct dirent *dit;
335
336         strncat(directory, path, strlen(path));
337         strncat(directory, device, min(strlen(device), 8));
338         strncat(directory, subdir, strlen(subdir));
339
340         status = access(directory, F_OK);
341         if (status == -1) {
342                 printf(" There are no IO port available\n");
343                 return;
344         }
345
346         dip = opendir(directory);
347         if (dip == NULL) {
348                 perror("opendir");
349                 return;
350         }
351
352         while ((dit = readdir(dip)) != NULL) {
353                 if (strcmp(dit->d_name, ".") && strcmp(dit->d_name, "..")) {
354                         printf(" %s\n", dit->d_name);
355                 }
356         }
357
358         status = closedir(dip);
359         if (status == -1) {
360                 perror("closedir()");
361                 return;
362         }
363
364 }
365
366
367 static void run_simple_tests(char* dev_name)
368 {
369         int status;
370         int device_fd;
371         char buff[BUFF_SMALL];
372         memset(buff, '\0', BUFF_SMALL);
373
374         strncat(buff, "/dev/", 5);
375         strncat(buff, dev_name, min(strlen(dev_name), 8));
376
377         printf("Opening %s\n", buff);
378
379         device_fd = open_device(buff);
380         if (device_fd != -1) {
381                 printf("Tring to enable interrupts\n");
382                 status = enable_interrupts(device_fd);
383                 if (status != -1) {
384                         printf(" Probably OK\n");
385                 }
386
387                 printf("Tring to disable interrupts\n");
388                 status = disable_interrupts(device_fd);
389                 if (status != -1) {
390                         printf(" Probably OK\n");
391                 }
392         }
393
394
395         printf("Checking for available memory regions exported by the UIO driver\n");
396         list_available_mem_regions(dev_name);
397
398         printf("Checking for available IO ports exported by the UIO driver\n");
399         list_available_io_ports(dev_name);
400 }
401
402 static int mmap_regions(mf624_state_t* mfst)
403 {
404         if (bar_mapping_fill(&mfst->bar0, mfst->uio_dev, 0) < 0) {
405                 fprintf(stderr, "%s bar0 mapping fill failed\n", mfst->uio_dev);
406                 return -1;
407         }
408
409         if (bar_mapping_fill(&mfst->bar2, mfst->uio_dev, 1) < 0) {
410                 fprintf(stderr, "%s bar2 mapping fill failed\n", mfst->uio_dev);
411                 return -1;
412         }
413
414         if (bar_mapping_fill(&mfst->bar4, mfst->uio_dev, 2) < 0) {
415                 fprintf(stderr, "%s bar4 mapping fill failed\n", mfst->uio_dev);
416                 return -1;
417         }
418
419         if (bar_mapping_setup(&mfst->bar0, mfst->device_fd) < 0) {
420                 fprintf(stderr, "%s bar0 mapping setup failed\n", mfst->uio_dev);
421                 return -1;
422         }
423
424         if (bar_mapping_setup(&mfst->bar2, mfst->device_fd) < 0) {
425                 fprintf(stderr, "%s bar2 mapping setup failed\n", mfst->uio_dev);
426                 return -1;
427         }
428
429         if (bar_mapping_setup(&mfst->bar4, mfst->device_fd) < 0) {
430                 fprintf(stderr, "%s bar4 mapping setup failed\n", mfst->uio_dev);
431                 return -1;
432         }
433
434 #if 1
435         printf("bar0.phys_addr = %"PRIxPTR"\n", mfst->bar0.phys_addr);
436         printf("bar2.phys_addr = %"PRIxPTR"\n", mfst->bar2.phys_addr);
437         printf("bar4.phys_addr = %"PRIxPTR"\n", mfst->bar4.phys_addr);
438
439         printf("bar0.virt_addr = %"PRIxPTR"\n", mfst->bar0.virt_addr);
440         printf("bar2.virt_addr = %"PRIxPTR"\n", mfst->bar2.virt_addr);
441         printf("bar4.virt_addr = %"PRIxPTR"\n", mfst->bar4.virt_addr);
442 #endif
443
444         return 0;
445 }
446
447
448
449 /**
450  * Try to initialize the MF624 UIO driver.
451  *
452  * If and only if S is not NULL and initialization fails, then
453  * ssSetErrorStatus() gets called to report the error via Simulink
454  * interface.
455  *
456  * @param S Pointer to SimStruct for error reporting or NULL.
457  *
458  * @return Zero in case of success, -1 in case of error.
459  */
460 int mf624_init(SimStruct *S)
461 {
462         if (mfst==NULL) {
463                 mfst = malloc(sizeof(mf624_state_t));
464                 char fn[32];
465                 mfst->uio_dev = UIO;
466                 snprintf(fn, sizeof(fn), "/dev/%s", mfst->uio_dev);
467
468                 mfst->device_fd = open_device(fn);
469                 if (mfst->device_fd < 0) {
470                         if (S) ssSetErrorStatus(S,"/dev/" UIO ": open failed");
471                         goto free;
472                 }
473                 if (mmap_regions(mfst) < 0) {
474                         if (S) ssSetErrorStatus(S,"/dev/" UIO ": mmap_regions failed");
475                         goto close;
476                 }
477         }
478         mfst_refcnt++;
479         return 0;
480 close:
481         close(mfst->device_fd);
482 free:
483         free(mfst);
484         mfst = NULL;
485         return -1;
486 }
487
488 int mf624_done()
489 {
490         if (mfst) {
491                 if (--mfst_refcnt == 0) {
492                         close(mfst->device_fd);
493                         bar_mapping_destroy(&mfst->bar0);
494                         bar_mapping_destroy(&mfst->bar2);
495                         bar_mapping_destroy(&mfst->bar4);
496                         free(mfst);
497                         mfst = NULL;
498                 }
499         }
500
501         return 0;
502 }
503
504
505 /**
506  * Check whether MF624 card is initialized.
507  *
508  * @param S
509  *
510  * @return Zero if MF624 is initialized, -1 othewise.
511  */
512 int mf624_check(SimStruct *S)
513 {
514         if (mfst==NULL) {
515                 if (S) ssSetErrorStatus(S, "MF624 is not initialized");
516                 return -1;
517         }
518         else
519                 return 0;
520 }
521
522 /*int main(int argc, char* argv[])
523 {
524         mf624_state_t* mfst = &mf624_state;
525         char buff[BUFF_SMALL];
526         memset(buff, '\0', BUFF_SMALL);
527
528         if (argc < 2) {
529                 printf("Usage: %s UIO_DEVICE\n   UIO_DEVICE\tname of uio device in /dev\n", argv[0]);
530                 return 1;
531         }
532
533         mfst->uio_dev = argv[1];
534
535         strncat(buff, "/dev/", 5);
536         strncat(buff, mfst->uio_dev, sizeof(buff) - 6);
537
538         mfst->device_fd = open_device(buff);
539         if (mfst->device_fd < 0) {
540                 fprintf(stderr, "%s open failed (%s)!\n", mfst->uio_dev, strerror(errno));
541                 return 2;
542         }
543         if (mmap_regions(mfst) < 0) {
544                 fprintf(stderr, "%s mmap_regions failed (%s)!\n", mfst->uio_dev, strerror(errno));
545                 return 2;
546         }
547
548         DAC_enable(mfst);
549
550         while (1){
551                 printf("Reading DIO: ");
552                 print_8bin(DIO_read(mfst));
553                 sleep(1);
554
555                 printf("Setting DA1 to 10 V\n");
556                 DAC_write(mfst, DA1, 0x3FFF);
557                 sleep(1);
558
559                 printf("Reading ADC0: ");
560                 printf("%f V\n", ADC_read(mfst, AD0));
561                 sleep(1);
562
563                 printf("Reading ADC1: ");
564                 printf("%f V\n", ADC_read(mfst, AD1));
565                 sleep(1);
566
567                 printf("Setting DIO to 0xff\n");
568                 DIO_write(mfst, 0xff);
569                 sleep(1);
570
571                 printf("Setting DIO to 0x00\n");
572                 DIO_write(mfst, 0x00);
573                 sleep(1);
574
575                 printf("Setting DA1 to 5 V\n");
576                 DAC_write(mfst, DA1, 0x3000);
577                 sleep(1);
578
579                 printf("Reading ADC0: ");
580                 printf("%f V\n", ADC_read(mfst, AD0));
581                 sleep(1);
582
583                 printf("Reading ADC1: ");
584                 printf("%f V\n", ADC_read(mfst, AD1));
585                 sleep(1);
586                 printf("----------------------\n\n");
587         }
588
589
590         return 0;
591 }*/