hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/*
 * Analogy for Linux, buffer related features
 *
 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
 * Copyright (C) 2008 Alexis Berlemont <alexis.berlemont@free.fr>
 *
 * Xenomai is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Xenomai is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Xenomai; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifndef _COBALT_RTDM_ANALOGY_BUFFER_H
#define _COBALT_RTDM_ANALOGY_BUFFER_H
 
#include <linux/version.h>
#include <linux/mm.h>
#include <rtdm/driver.h>
#include <rtdm/uapi/analogy.h>
#include <rtdm/analogy/rtdm_helpers.h>
#include <rtdm/analogy/context.h>
#include <rtdm/analogy/command.h>
#include <rtdm/analogy/subdevice.h>
 
/* --- Events bits / flags --- */
 
#define A4L_BUF_EOBUF_NR 0
#define A4L_BUF_EOBUF (1 << A4L_BUF_EOBUF_NR)
 
#define A4L_BUF_ERROR_NR 1
#define A4L_BUF_ERROR (1 << A4L_BUF_ERROR_NR)
 
#define A4L_BUF_EOA_NR 2
#define A4L_BUF_EOA (1 << A4L_BUF_EOA_NR)
 
/* --- Status bits / flags --- */
 
#define A4L_BUF_BULK_NR 8
#define A4L_BUF_BULK (1 << A4L_BUF_BULK_NR)
 
#define A4L_BUF_MAP_NR 9
#define A4L_BUF_MAP (1 << A4L_BUF_MAP_NR)
 
 
/* Buffer descriptor structure */
struct a4l_buffer {
 
   /* Added by the structure update */
   struct a4l_subdevice *subd;
 
   /* Buffer's first virtual page pointer */
   void *buf;
 
   /* Buffer's global size */
   unsigned long size;
   /* Tab containing buffer's pages pointers */
   unsigned long *pg_list;
 
   /* RT/NRT synchronization element */
   struct a4l_sync sync;
 
   /* Counters needed for transfer */
   unsigned long end_count;
   unsigned long prd_count;
   unsigned long cns_count;
   unsigned long tmp_count;
 
   /* Status + events occuring during transfer */
   unsigned long flags;
 
   /* Command on progress */
   struct a4l_cmd_desc *cur_cmd;
 
   /* Munge counter */
   unsigned long mng_count;
 
   /* Theshold below which the user process should not be
      awakened */
   unsigned long wake_count;
};
 
static inline void __dump_buffer_counters(struct a4l_buffer *buf)
{
   __a4l_dbg(1, core_dbg, "a4l_buffer=0x%p, p=0x%p \n", buf, buf->buf);
   __a4l_dbg(1, core_dbg, "end=%06ld, prd=%06ld, cns=%06ld, tmp=%06ld \n",
       buf->end_count, buf->prd_count, buf->cns_count, buf->tmp_count);
}
 
/* --- Static inline functions related with
   user<->kernel data transfers --- */
 
/* The function __produce is an inline function which copies data into
   the asynchronous buffer and takes care of the non-contiguous issue
   when looping. This function is used in read and write operations */
static inline int __produce(struct a4l_device_context *cxt,
               struct a4l_buffer *buf, void *pin, unsigned long count)
{
   unsigned long start_ptr = (buf->prd_count % buf->size);
   struct rtdm_fd *fd = rtdm_private_to_fd(cxt);
   unsigned long tmp_cnt = count;
   int ret = 0;
 
   while (ret == 0 && tmp_cnt != 0) {
       /* Check the data copy can be performed contiguously */
       unsigned long blk_size = (start_ptr + tmp_cnt > buf->size) ?
           buf->size - start_ptr : tmp_cnt;
 
       /* Perform the copy */
       if (cxt == NULL)
           memcpy(buf->buf + start_ptr, pin, blk_size);
       else
           ret = rtdm_safe_copy_from_user(fd,
                              buf->buf + start_ptr,
                              pin, blk_size);
 
       /* Update pointers/counts */
       pin += blk_size;
       tmp_cnt -= blk_size;
       start_ptr = 0;
   }
 
   return ret;
}
 
/* The function __consume is an inline function which copies data from
   the asynchronous buffer and takes care of the non-contiguous issue
   when looping. This function is used in read and write operations */
static inline int __consume(struct a4l_device_context *cxt,
               struct a4l_buffer *buf, void *pout, unsigned long count)
{
   unsigned long start_ptr = (buf->cns_count % buf->size);
   struct rtdm_fd *fd = rtdm_private_to_fd(cxt);
   unsigned long tmp_cnt = count;
   int ret = 0;
 
   while (ret == 0 && tmp_cnt != 0) {
       /* Check the data copy can be performed contiguously */
       unsigned long blk_size = (start_ptr + tmp_cnt > buf->size) ?
           buf->size - start_ptr : tmp_cnt;
 
       /* Perform the copy */
       if (cxt == NULL)
           memcpy(pout, buf->buf + start_ptr, blk_size);
       else
           ret = rtdm_safe_copy_to_user(fd,
                            pout,
                            buf->buf + start_ptr,
                            blk_size);
 
       /* Update pointers/counts */
       pout += blk_size;
       tmp_cnt -= blk_size;
       start_ptr = 0;
   }
 
   return ret;
}
 
/* The function __munge is an inline function which calls the
   subdevice specific munge callback on contiguous windows within the
   whole buffer. This function is used in read and write operations */
static inline void __munge(struct a4l_subdevice * subd,
              void (*munge) (struct a4l_subdevice *,
                     void *, unsigned long),
              struct a4l_buffer * buf, unsigned long count)
{
   unsigned long start_ptr = (buf->mng_count % buf->size);
   unsigned long tmp_cnt = count;
 
   while (tmp_cnt != 0) {
       /* Check the data copy can be performed contiguously */
       unsigned long blk_size = (start_ptr + tmp_cnt > buf->size) ?
           buf->size - start_ptr : tmp_cnt;
 
       /* Perform the munge operation */
       munge(subd, buf->buf + start_ptr, blk_size);
 
       /* Update the start pointer and the count */
       tmp_cnt -= blk_size;
       start_ptr = 0;
   }
}
 
/* The function __handle_event can only be called from process context
   (not interrupt service routine). It allows the client process to
   retrieve the buffer status which has been updated by the driver */
static inline int __handle_event(struct a4l_buffer * buf)
{
   int ret = 0;
 
   /* The event "End of acquisition" must not be cleaned
      before the complete flush of the buffer */
   if (test_bit(A4L_BUF_EOA_NR, &buf->flags))
       ret = -ENOENT;
 
   if (test_bit(A4L_BUF_ERROR_NR, &buf->flags))
       ret = -EPIPE;
 
   return ret;
}
 
/* --- Counters management functions --- */
 
/* Here, we may wonder why we need more than two counters / pointers.
 
   Theoretically, we only need two counters (or two pointers):
   - one which tells where the reader should be within the buffer
   - one which tells where the writer should be within the buffer
 
   With these two counters (or pointers), we just have to check that
   the writer does not overtake the reader inside the ring buffer
   BEFORE any read / write operations.
 
   However, if one element is a DMA controller, we have to be more
   careful. Generally a DMA transfer occurs like this:
   DMA shot
      |-> then DMA interrupt
    |-> then DMA soft handler which checks the counter
 
   So, the checkings occur AFTER the write operations.
 
   Let's take an example: the reader is a software task and the writer
   is a DMA controller. At the end of the DMA shot, the write counter
   is higher than the read counter. Unfortunately, a read operation
   occurs between the DMA shot and the DMA interrupt, so the handler
   will not notice that an overflow occured.
 
   That is why tmp_count comes into play: tmp_count records the
   read/consumer current counter before the next DMA shot and once the
   next DMA shot is done, we check that the updated writer/producer
   counter is not higher than tmp_count. Thus we are sure that the DMA
   writer has not overtaken the reader because it was not able to
   overtake the n-1 value. */
 
static inline int __pre_abs_put(struct a4l_buffer * buf, unsigned long count)
{
   if (count - buf->tmp_count > buf->size) {
       set_bit(A4L_BUF_ERROR_NR, &buf->flags);
       return -EPIPE;
   }
 
   buf->tmp_count = buf->cns_count;
 
   return 0;
}
 
static inline int __pre_put(struct a4l_buffer * buf, unsigned long count)
{
   return __pre_abs_put(buf, buf->tmp_count + count);
}
 
static inline int __pre_abs_get(struct a4l_buffer * buf, unsigned long count)
{
   /* The first time, we expect the buffer to be properly filled
   before the trigger occurence; by the way, we need tmp_count to
   have been initialized and tmp_count is updated right here */
   if (buf->tmp_count == 0 || buf->cns_count == 0)
       goto out;
 
   /* At the end of the acquisition, the user application has
   written the defined amount of data into the buffer; so the
   last time, the DMA channel can easily overtake the tmp
   frontier because no more data were sent from user space;
   therefore no useless alarm should be sent */
   if (buf->end_count != 0 && (long)(count - buf->end_count) > 0)
       goto out;
 
   /* Once the exception are passed, we check that the DMA
   transfer has not overtaken the last record of the production
   count (tmp_count was updated with prd_count the last time
   __pre_abs_get was called). We must understand that we cannot
   compare the current DMA count with the current production
   count because even if, right now, the production count is
   higher than the DMA count, it does not mean that the DMA count
   was not greater a few cycles before; in such case, the DMA
   channel would have retrieved the wrong data */
   if ((long)(count - buf->tmp_count) > 0) {
       set_bit(A4L_BUF_ERROR_NR, &buf->flags);
       return -EPIPE;
   }
 
out:
   buf->tmp_count = buf->prd_count;
 
   return 0;
}
 
static inline int __pre_get(struct a4l_buffer * buf, unsigned long count)
{
   return __pre_abs_get(buf, buf->tmp_count + count);
}
 
static inline int __abs_put(struct a4l_buffer * buf, unsigned long count)
{
   unsigned long old = buf->prd_count;
 
   if ((long)(buf->prd_count - count) >= 0)
       return -EINVAL;
 
   buf->prd_count = count;
 
   if ((old / buf->size) != (count / buf->size))
       set_bit(A4L_BUF_EOBUF_NR, &buf->flags);
 
   if (buf->end_count != 0 && (long)(count - buf->end_count) >= 0)
       set_bit(A4L_BUF_EOA_NR, &buf->flags);
 
   return 0;
}
 
static inline int __put(struct a4l_buffer * buf, unsigned long count)
{
   return __abs_put(buf, buf->prd_count + count);
}
 
static inline int __abs_get(struct a4l_buffer * buf, unsigned long count)
{
   unsigned long old = buf->cns_count;
 
   if ((long)(buf->cns_count - count) >= 0)
       return -EINVAL;
 
   buf->cns_count = count;
 
   if ((old / buf->size) != count / buf->size)
       set_bit(A4L_BUF_EOBUF_NR, &buf->flags);
 
   if (buf->end_count != 0 && (long)(count - buf->end_count) >= 0)
       set_bit(A4L_BUF_EOA_NR, &buf->flags);
 
   return 0;
}
 
static inline int __get(struct a4l_buffer * buf, unsigned long count)
{
   return __abs_get(buf, buf->cns_count + count);
}
 
static inline unsigned long __count_to_put(struct a4l_buffer * buf)
{
   unsigned long ret;
 
   if ((long) (buf->size + buf->cns_count - buf->prd_count) > 0)
       ret = buf->size + buf->cns_count - buf->prd_count;
   else
       ret = 0;
 
   return ret;
}
 
static inline unsigned long __count_to_get(struct a4l_buffer * buf)
{
   unsigned long ret;
 
   /* If the acquisition is unlimited (end_count == 0), we must
      not take into account end_count */
   if (buf->end_count == 0 || (long)(buf->end_count - buf->prd_count) > 0)
       ret = buf->prd_count;
   else
       ret = buf->end_count;
 
   if ((long)(ret - buf->cns_count) > 0)
       ret -= buf->cns_count;
   else
       ret = 0;
 
   return ret;
}
 
static inline unsigned long __count_to_end(struct a4l_buffer * buf)
{
   unsigned long ret = buf->end_count - buf->cns_count;
 
   if (buf->end_count == 0)
       return ULONG_MAX;
 
   return ((long)ret) < 0 ? 0 : ret;
}
 
/* --- Buffer internal functions --- */
 
int a4l_alloc_buffer(struct a4l_buffer *buf_desc, int buf_size);
 
void a4l_free_buffer(struct a4l_buffer *buf_desc);
 
void a4l_init_buffer(struct a4l_buffer * buf_desc);
 
void a4l_cleanup_buffer(struct a4l_buffer * buf_desc);
 
int a4l_setup_buffer(struct a4l_device_context *cxt, struct a4l_cmd_desc *cmd);
 
void a4l_cancel_buffer(struct a4l_device_context *cxt);
 
int a4l_buf_prepare_absput(struct a4l_subdevice *subd,
              unsigned long count);
 
int a4l_buf_commit_absput(struct a4l_subdevice *subd,
             unsigned long count);
 
int a4l_buf_prepare_put(struct a4l_subdevice *subd,
           unsigned long count);
 
int a4l_buf_commit_put(struct a4l_subdevice *subd,
              unsigned long count);
 
int a4l_buf_put(struct a4l_subdevice *subd,
       void *bufdata, unsigned long count);
 
int a4l_buf_prepare_absget(struct a4l_subdevice *subd,
              unsigned long count);
 
int a4l_buf_commit_absget(struct a4l_subdevice *subd,
             unsigned long count);
 
int a4l_buf_prepare_get(struct a4l_subdevice *subd,
           unsigned long count);
 
int a4l_buf_commit_get(struct a4l_subdevice *subd,
              unsigned long count);
 
int a4l_buf_get(struct a4l_subdevice *subd,
       void *bufdata, unsigned long count);
 
int a4l_buf_evt(struct a4l_subdevice *subd, unsigned long evts);
 
unsigned long a4l_buf_count(struct a4l_subdevice *subd);
 
/* --- Current Command management function --- */
 
static inline struct a4l_cmd_desc *a4l_get_cmd(struct a4l_subdevice *subd)
{
   return (subd->buf) ? subd->buf->cur_cmd : NULL;
}
 
/* --- Munge related function --- */
 
int a4l_get_chan(struct a4l_subdevice *subd);
 
/* --- IOCTL / FOPS functions --- */
 
int a4l_ioctl_mmap(struct a4l_device_context * cxt, void *arg);
int a4l_ioctl_bufcfg(struct a4l_device_context * cxt, void *arg);
int a4l_ioctl_bufcfg2(struct a4l_device_context * cxt, void *arg);
int a4l_ioctl_bufinfo(struct a4l_device_context * cxt, void *arg);
int a4l_ioctl_bufinfo2(struct a4l_device_context * cxt, void *arg);
int a4l_ioctl_poll(struct a4l_device_context * cxt, void *arg);
ssize_t a4l_read_buffer(struct a4l_device_context * cxt, void *bufdata, size_t nbytes);
ssize_t a4l_write_buffer(struct a4l_device_context * cxt, const void *bufdata, size_t nbytes);
int a4l_select(struct a4l_device_context *cxt,
          rtdm_selector_t *selector,
          enum rtdm_selecttype type, unsigned fd_index);
 
#endif /* !_COBALT_RTDM_ANALOGY_BUFFER_H */