PX4 Firmware
PX4 Autopilot Software http://px4.io
CDev.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file CDev.cpp
36  *
37  * Character device base class.
38  */
39 
40 #include "CDev.hpp"
41 
42 #include <cstring>
43 
44 #include <px4_platform_common/posix.h>
45 #include <drivers/drv_device.h>
46 
47 namespace cdev
48 {
49 
50 CDev::CDev(const char *devname) :
51  _devname(devname)
52 {
53  PX4_DEBUG("CDev::CDev");
54 
55  int ret = px4_sem_init(&_lock, 0, 1);
56 
57  if (ret != 0) {
58  PX4_DEBUG("SEM INIT FAIL: ret %d", ret);
59  }
60 }
61 
63 {
64  PX4_DEBUG("CDev::~CDev");
65 
66  if (_registered) {
68  }
69 
70  if (_pollset) {
71  delete[](_pollset);
72  }
73 
74  px4_sem_destroy(&_lock);
75 }
76 
77 int
78 CDev::register_class_devname(const char *class_devname)
79 {
80  PX4_DEBUG("CDev::register_class_devname %s", class_devname);
81 
82  if (class_devname == nullptr) {
83  return -EINVAL;
84  }
85 
86  int class_instance = 0;
87  int ret = -ENOSPC;
88 
89  while (class_instance < 4) {
90  char name[32];
91  snprintf(name, sizeof(name), "%s%d", class_devname, class_instance);
92  ret = register_driver(name, &fops, 0666, (void *)this);
93 
94  if (ret == OK) {
95  break;
96  }
97 
98  class_instance++;
99  }
100 
101  if (class_instance == 4) {
102  return ret;
103  }
104 
105  return class_instance;
106 }
107 
108 int
109 CDev::unregister_class_devname(const char *class_devname, unsigned class_instance)
110 {
111  PX4_DEBUG("CDev::unregister_class_devname");
112 
113  char name[32];
114  snprintf(name, sizeof(name), "%s%u", class_devname, class_instance);
115  return unregister_driver(name);
116 }
117 
118 int
120 {
121  PX4_DEBUG("CDev::init");
122 
123  int ret = PX4_OK;
124 
125  // now register the driver
126  if (_devname != nullptr) {
127  ret = register_driver(_devname, &fops, 0666, (void *)this);
128 
129  if (ret == PX4_OK) {
130  _registered = true;
131  }
132  }
133 
134  return ret;
135 }
136 
137 /*
138  * Default implementations of the character device interface
139  */
140 int
142 {
143  PX4_DEBUG("CDev::open");
144  int ret = PX4_OK;
145 
146  lock();
147  /* increment the open count */
148  _open_count++;
149 
150  if (_open_count == 1) {
151 
152  /* first-open callback may decline the open */
153  ret = open_first(filep);
154 
155  if (ret != PX4_OK) {
156  _open_count--;
157  }
158  }
159 
160  unlock();
161 
162  return ret;
163 }
164 
165 int
167 {
168  PX4_DEBUG("CDev::close");
169  int ret = PX4_OK;
170 
171  lock();
172 
173  if (_open_count > 0) {
174  /* decrement the open count */
175  _open_count--;
176 
177  /* callback cannot decline the close */
178  if (_open_count == 0) {
179  ret = close_last(filep);
180  }
181 
182  } else {
183  ret = -EBADF;
184  }
185 
186  unlock();
187 
188  return ret;
189 }
190 
191 int
192 CDev::ioctl(file_t *filep, int cmd, unsigned long arg)
193 {
194  PX4_DEBUG("CDev::ioctl");
195  int ret = -ENOTTY;
196 
197  switch (cmd) {
198 
199  /* fetch a pointer to the driver's private data */
200  case DIOC_GETPRIV:
201  *(void **)(uintptr_t)arg = (void *)this;
202  ret = PX4_OK;
203  break;
204 
205  default:
206  break;
207  }
208 
209  return ret;
210 }
211 
212 int
213 CDev::poll(file_t *filep, px4_pollfd_struct_t *fds, bool setup)
214 {
215  PX4_DEBUG("CDev::Poll %s", setup ? "setup" : "teardown");
216  int ret = PX4_OK;
217 
218  if (setup) {
219  /*
220  * Save the file pointer in the pollfd for the subclass'
221  * benefit.
222  */
223  fds->priv = (void *)filep;
224  PX4_DEBUG("CDev::poll: fds->priv = %p", filep);
225 
226  /*
227  * Lock against poll_notify() and possibly other callers (protect _pollset).
228  */
229  ATOMIC_ENTER;
230 
231  /*
232  * Try to store the fds for later use and handle array resizing.
233  */
234  while ((ret = store_poll_waiter(fds)) == -ENFILE) {
235 
236  // No free slot found. Resize the pollset. This is expensive, but it's only needed initially.
237 
238  if (_max_pollwaiters >= 256 / 2) { //_max_pollwaiters is uint8_t
239  ret = -ENOMEM;
240  break;
241  }
242 
243  const uint8_t new_count = _max_pollwaiters > 0 ? _max_pollwaiters * 2 : 1;
244  px4_pollfd_struct_t **prev_pollset = _pollset;
245 
246 #ifdef __PX4_NUTTX
247  // malloc uses a semaphore, we need to call it enabled IRQ's
248  px4_leave_critical_section(flags);
249 #endif
250  px4_pollfd_struct_t **new_pollset = new px4_pollfd_struct_t *[new_count];
251 
252 #ifdef __PX4_NUTTX
253  flags = px4_enter_critical_section();
254 #endif
255 
256  if (prev_pollset == _pollset) {
257  // no one else updated the _pollset meanwhile, so we're good to go
258  if (!new_pollset) {
259  ret = -ENOMEM;
260  break;
261  }
262 
263  if (_max_pollwaiters > 0) {
264  memset(new_pollset + _max_pollwaiters, 0, sizeof(px4_pollfd_struct_t *) * (new_count - _max_pollwaiters));
265  memcpy(new_pollset, _pollset, sizeof(px4_pollfd_struct_t *) * _max_pollwaiters);
266  }
267 
268  _pollset = new_pollset;
269  _pollset[_max_pollwaiters] = fds;
270  _max_pollwaiters = new_count;
271 
272  // free the previous _pollset (we need to unlock here which is fine because we don't access _pollset anymore)
273 #ifdef __PX4_NUTTX
274  px4_leave_critical_section(flags);
275 #endif
276 
277  if (prev_pollset) {
278  delete[](prev_pollset);
279  }
280 
281 #ifdef __PX4_NUTTX
282  flags = px4_enter_critical_section();
283 #endif
284 
285  // Success
286  ret = PX4_OK;
287  break;
288  }
289 
290 #ifdef __PX4_NUTTX
291  px4_leave_critical_section(flags);
292 #endif
293  // We have to retry
294  delete[] new_pollset;
295 #ifdef __PX4_NUTTX
296  flags = px4_enter_critical_section();
297 #endif
298  }
299 
300  if (ret == PX4_OK) {
301 
302  /*
303  * Check to see whether we should send a poll notification
304  * immediately.
305  */
306  fds->revents |= fds->events & poll_state(filep);
307 
308  /* yes? post the notification */
309  if (fds->revents != 0) {
310  px4_sem_post(fds->sem);
311  }
312 
313  }
314 
315  ATOMIC_LEAVE;
316 
317  } else {
318  ATOMIC_ENTER;
319  /*
320  * Handle a teardown request.
321  */
322  ret = remove_poll_waiter(fds);
323  ATOMIC_LEAVE;
324  }
325 
326  return ret;
327 }
328 
329 void
331 {
332  PX4_DEBUG("CDev::poll_notify events = %0x", events);
333 
334  /* lock against poll() as well as other wakeups */
335  ATOMIC_ENTER;
336 
337  for (unsigned i = 0; i < _max_pollwaiters; i++) {
338  if (nullptr != _pollset[i]) {
339  poll_notify_one(_pollset[i], events);
340  }
341  }
342 
343  ATOMIC_LEAVE;
344 }
345 
346 void
347 CDev::poll_notify_one(px4_pollfd_struct_t *fds, pollevent_t events)
348 {
349  PX4_DEBUG("CDev::poll_notify_one");
350 
351  /* update the reported event set */
352  fds->revents |= fds->events & events;
353 
354  PX4_DEBUG(" Events fds=%p %0x %0x %0x", fds, fds->revents, fds->events, events);
355 
356  if (fds->revents != 0) {
357  px4_sem_post(fds->sem);
358  }
359 }
360 
361 int
362 CDev::store_poll_waiter(px4_pollfd_struct_t *fds)
363 {
364  // Look for a free slot.
365  PX4_DEBUG("CDev::store_poll_waiter");
366 
367  for (unsigned i = 0; i < _max_pollwaiters; i++) {
368  if (nullptr == _pollset[i]) {
369 
370  /* save the pollfd */
371  _pollset[i] = fds;
372 
373  return PX4_OK;
374  }
375  }
376 
377  return -ENFILE;
378 }
379 
380 int
381 CDev::remove_poll_waiter(px4_pollfd_struct_t *fds)
382 {
383  PX4_DEBUG("CDev::remove_poll_waiter");
384 
385  for (unsigned i = 0; i < _max_pollwaiters; i++) {
386  if (fds == _pollset[i]) {
387 
388  _pollset[i] = nullptr;
389  return PX4_OK;
390 
391  }
392  }
393 
394  PX4_DEBUG("poll: bad fd state");
395  return -EINVAL;
396 }
397 
399 {
400  int retval = PX4_OK;
401 
402  if (_registered) {
404  _registered = false;
405 
406  } else {
407  retval = -ENODEV;
408  }
409 
410  if (_devname != nullptr) {
411  free((void *)_devname);
412  _devname = nullptr;
413 
414  } else {
415  retval = -ENODEV;
416  }
417 
418  return retval;
419 }
420 
421 } // namespace cdev
uint16_t _open_count
number of successful opens
Definition: CDev.hpp:292
int unregister_driver_and_memory()
First, unregisters the driver.
Definition: CDev.cpp:398
virtual int close_last(file_t *filep)
Notification of the last close.
Definition: CDev.hpp:234
const char * _devname
device node name
Definition: CDev.hpp:285
virtual int open(file_t *filep)
Handle an open of the device.
Definition: CDev.cpp:141
px4_sem_t _lock
lock to protect access to all class members (also for derived classes)
Definition: CDev.hpp:271
virtual int register_class_devname(const char *class_devname)
Register a class device name, automatically adding device class instance suffix if need be...
Definition: CDev.cpp:78
void lock()
Take the driver lock.
Definition: CDev.hpp:264
virtual int init()
Definition: CDev.cpp:119
virtual pollevent_t poll_state(file_t *filep)
Check the current state of the device for poll events from the perspective of the file...
Definition: CDev.hpp:190
virtual void poll_notify_one(px4_pollfd_struct_t *fds, pollevent_t events)
Internal implementation of poll_notify.
Definition: CDev.cpp:347
static const px4_file_operations_t fops
Pointer to the default cdev file operations table; useful for registering clone devices etc...
Definition: CDev.hpp:176
virtual int close(file_t *filep)
Handle a close of the device.
Definition: CDev.cpp:166
virtual int open_first(file_t *filep)
Notification of the first open.
Definition: CDev.hpp:221
virtual void poll_notify(pollevent_t events)
Report new poll events.
Definition: CDev.cpp:330
Generic device / sensor interface.
uint8_t _max_pollwaiters
size of the _pollset array
Definition: CDev.hpp:291
px4_pollfd_struct_t ** _pollset
Definition: CDev.hpp:287
int unregister_driver(const char *name)
virtual int unregister_class_devname(const char *class_devname, unsigned class_instance)
Register a class device name, automatically adding device class instance suffix if need be...
Definition: CDev.cpp:109
int remove_poll_waiter(px4_pollfd_struct_t *fds)
Remove a poll waiter.
Definition: CDev.cpp:381
int register_driver(const char *name, const cdev::px4_file_operations_t *fops, cdev::mode_t mode, void *data)
virtual ~CDev()
Definition: CDev.cpp:62
#define ATOMIC_LEAVE
Definition: CDev.cpp:47
const char * name
Definition: tests_main.c:58
int store_poll_waiter(px4_pollfd_struct_t *fds)
Store a pollwaiter in a slot where we can find it later.
Definition: CDev.cpp:362
bool _registered
true if device name was registered
Definition: CDev.hpp:289
CDev(const char *devname)
Constructor.
Definition: CDev.cpp:50
#define OK
Definition: uavcan_main.cpp:71
virtual int ioctl(file_t *filep, int cmd, unsigned long arg)
Perform an ioctl operation on the device.
Definition: CDev.cpp:192
void unlock()
Release the driver lock.
Definition: CDev.hpp:269
#define ATOMIC_ENTER
virtual int poll(file_t *filep, px4_pollfd_struct_t *fds, bool setup)
Perform a poll setup/teardown operation.
Definition: CDev.cpp:213