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