IgH EtherCAT Master  1.5.2
cdev.c
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * $Id$
4  *
5  * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
6  *
7  * This file is part of the IgH EtherCAT Master.
8  *
9  * The IgH EtherCAT Master is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License version 2, as
11  * published by the Free Software Foundation.
12  *
13  * The IgH EtherCAT Master is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with the IgH EtherCAT Master; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21  *
22  * ---
23  *
24  * The license mentioned above concerns the source code only. Using the
25  * EtherCAT technology and brand is only permitted in compliance with the
26  * industrial property and similar rights of Beckhoff Automation GmbH.
27  *
28  *****************************************************************************/
29 
35 /*****************************************************************************/
36 
37 #include <linux/module.h>
38 #include <linux/vmalloc.h>
39 #include <linux/mm.h>
40 
41 #include "cdev.h"
42 #include "master.h"
43 #include "slave_config.h"
44 #include "voe_handler.h"
45 #include "ethernet.h"
46 #include "ioctl.h"
47 
50 #define DEBUG 0
51 
52 /*****************************************************************************/
53 
54 static int eccdev_open(struct inode *, struct file *);
55 static int eccdev_release(struct inode *, struct file *);
56 static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
57 static int eccdev_mmap(struct file *, struct vm_area_struct *);
58 
62 #define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23)
63 
64 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
65 static
66 #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 0)
67 vm_fault_t
68 #else
69 int
70 #endif
72 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
73  struct vm_area_struct *,
74 #endif
75  struct vm_fault *);
76 #else
77 static struct page *eccdev_vma_nopage(
78  struct vm_area_struct *, unsigned long, int *);
79 #endif
80 
81 /*****************************************************************************/
82 
85 static struct file_operations eccdev_fops = {
86  .owner = THIS_MODULE,
87  .open = eccdev_open,
88  .release = eccdev_release,
89  .unlocked_ioctl = eccdev_ioctl,
90  .mmap = eccdev_mmap
91 };
92 
95 struct vm_operations_struct eccdev_vm_ops = {
96 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
97  .fault = eccdev_vma_fault
98 #else
99  .nopage = eccdev_vma_nopage
100 #endif
101 };
102 
103 /*****************************************************************************/
104 
107 typedef struct {
109  ec_ioctl_context_t ctx;
111 
112 /*****************************************************************************/
113 
119  ec_cdev_t *cdev,
120  ec_master_t *master,
121  dev_t dev_num
122  )
123 {
124  int ret;
125 
126  cdev->master = master;
127 
128  cdev_init(&cdev->cdev, &eccdev_fops);
129  cdev->cdev.owner = THIS_MODULE;
130 
131  ret = cdev_add(&cdev->cdev,
132  MKDEV(MAJOR(dev_num), master->index), 1);
133  if (ret) {
134  EC_MASTER_ERR(master, "Failed to add character device!\n");
135  }
136 
137  return ret;
138 }
139 
140 /*****************************************************************************/
141 
145 {
146  cdev_del(&cdev->cdev);
147 }
148 
149 /******************************************************************************
150  * File operations
151  *****************************************************************************/
152 
155 int eccdev_open(struct inode *inode, struct file *filp)
156 {
157  ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
158  ec_cdev_priv_t *priv;
159 
160  priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
161  if (!priv) {
162  EC_MASTER_ERR(cdev->master,
163  "Failed to allocate memory for private data structure.\n");
164  return -ENOMEM;
165  }
166 
167  priv->cdev = cdev;
168  priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
169  priv->ctx.requested = 0;
170  priv->ctx.process_data = NULL;
171  priv->ctx.process_data_size = 0;
172 
173  filp->private_data = priv;
174 
175 #if DEBUG
176  EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
177 #endif
178  return 0;
179 }
180 
181 /*****************************************************************************/
182 
185 int eccdev_release(struct inode *inode, struct file *filp)
186 {
187  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
188  ec_master_t *master = priv->cdev->master;
189 
190  if (priv->ctx.requested) {
191  ecrt_release_master(master);
192  }
193 
194  if (priv->ctx.process_data) {
195  vfree(priv->ctx.process_data);
196  }
197 
198 #if DEBUG
199  EC_MASTER_DBG(master, 0, "File closed.\n");
200 #endif
201 
202  kfree(priv);
203  return 0;
204 }
205 
206 /*****************************************************************************/
207 
210 long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
211 {
212  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
213 
214 #if DEBUG
215  EC_MASTER_DBG(priv->cdev->master, 0,
216  "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
217  filp, cmd, _IOC_NR(cmd), arg);
218 #endif
219 
220  return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
221 }
222 
223 /*****************************************************************************/
224 
225 #ifndef VM_DONTDUMP
226 
228 #define VM_DONTDUMP VM_RESERVED
229 #endif
230 
239  struct file *filp,
240  struct vm_area_struct *vma
241  )
242 {
243  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
244 
245  EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
246 
247  vma->vm_ops = &eccdev_vm_ops;
248  vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
249  vma->vm_private_data = priv;
250 
251  return 0;
252 }
253 
254 /*****************************************************************************/
255 
256 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
257 
265 static
266 #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 0)
267 vm_fault_t
268 #else
269 int
270 #endif
272 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
273  struct vm_area_struct *vma,
274 #endif
275  struct vm_fault *vmf
276  )
277 {
278 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
279  struct vm_area_struct *vma = vmf->vma;
280 #endif
281  unsigned long offset = vmf->pgoff << PAGE_SHIFT;
282  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
283  struct page *page;
284 
285  if (offset >= priv->ctx.process_data_size) {
286  return VM_FAULT_SIGBUS;
287  }
288 
289  page = vmalloc_to_page(priv->ctx.process_data + offset);
290  if (!page) {
291  return VM_FAULT_SIGBUS;
292  }
293 
294  get_page(page);
295  vmf->page = page;
296 
297  EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault, virtual_address = %p,"
298 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
299  " offset = %lu, page = %p\n", (void*)vmf->address, offset, page);
300 #else
301  " offset = %lu, page = %p\n", vmf->virtual_address, offset, page);
302 #endif
303 
304  return 0;
305 }
306 
307 #else
308 
314 struct page *eccdev_vma_nopage(
315  struct vm_area_struct *vma,
317  unsigned long address,
318  int *type
319  )
320 {
321  unsigned long offset;
322  struct page *page = NOPAGE_SIGBUS;
323  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
324  ec_master_t *master = priv->cdev->master;
325 
326  offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
327 
328  if (offset >= priv->ctx.process_data_size)
329  return NOPAGE_SIGBUS;
330 
331  page = vmalloc_to_page(priv->ctx.process_data + offset);
332 
333  EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
334  " offset = %#lx, page = %p\n", address, offset, page);
335 
336  get_page(page);
337  if (type)
338  *type = VM_FAULT_MINOR;
339 
340  return page;
341 }
342 
343 #endif
344 
345 /*****************************************************************************/
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition: module.c:628
ec_master_t * master
Master owning the device.
Definition: cdev.h:50
#define VM_DONTDUMP
VM_RESERVED disappeared in 3.7.
Definition: cdev.c:228
static int eccdev_vma_fault(struct vm_fault *)
Page fault callback for a virtual memory area.
Definition: cdev.c:271
Private data structure for file handles.
Definition: cdev.c:107
struct cdev cdev
Character device.
Definition: cdev.h:51
ec_cdev_t * cdev
Character device.
Definition: cdev.c:108
EtherCAT master structure.
#define EC_MASTER_DBG(master, level, fmt, args...)
Convenience macro for printing master-specific debug messages to syslog.
Definition: master.h:106
Ethernet over EtherCAT (EoE)
static int eccdev_mmap(struct file *, struct vm_area_struct *)
Memory-map callback for the EtherCAT character device.
Definition: cdev.c:238
struct vm_operations_struct eccdev_vm_ops
Callbacks for a virtual memory area retrieved with ecdevc_mmap().
Definition: cdev.c:95
#define EC_MASTER_ERR(master, fmt, args...)
Convenience macro for printing master-specific errors to syslog.
Definition: master.h:80
static long eccdev_ioctl(struct file *, unsigned int, unsigned long)
Called when an ioctl() command is issued.
Definition: cdev.c:210
ec_ioctl_context_t ctx
Context.
Definition: cdev.c:109
static int eccdev_open(struct inode *, struct file *)
Called when the cdev is opened.
Definition: cdev.c:155
Vendor specific over EtherCAT protocol handler.
static int eccdev_release(struct inode *, struct file *)
Called when the cdev is closed.
Definition: cdev.c:185
void ec_cdev_clear(ec_cdev_t *cdev)
Destructor.
Definition: cdev.c:144
#define DEBUG
Set to 1 to enable device operations debugging.
Definition: cdev.c:50
EtherCAT master character device.
Definition: cdev.h:49
EtherCAT master character device IOCTL commands.
EtherCAT slave configuration structure.
unsigned int index
Index.
Definition: master.h:203
EtherCAT master.
Definition: master.h:202
EtherCAT master character device.
static struct file_operations eccdev_fops
File operation callbacks for the EtherCAT character device.
Definition: cdev.c:85
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
Constructor.
Definition: cdev.c:118