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 int eccdev_vma_fault(
66 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
67  struct vm_area_struct *,
68 #endif
69  struct vm_fault *);
70 #else
71 static struct page *eccdev_vma_nopage(
72  struct vm_area_struct *, unsigned long, int *);
73 #endif
74 
75 /*****************************************************************************/
76 
79 static struct file_operations eccdev_fops = {
80  .owner = THIS_MODULE,
81  .open = eccdev_open,
82  .release = eccdev_release,
83  .unlocked_ioctl = eccdev_ioctl,
84  .mmap = eccdev_mmap
85 };
86 
89 struct vm_operations_struct eccdev_vm_ops = {
90 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
91  .fault = eccdev_vma_fault
92 #else
93  .nopage = eccdev_vma_nopage
94 #endif
95 };
96 
97 /*****************************************************************************/
98 
101 typedef struct {
103  ec_ioctl_context_t ctx;
105 
106 /*****************************************************************************/
107 
113  ec_cdev_t *cdev,
114  ec_master_t *master,
115  dev_t dev_num
116  )
117 {
118  int ret;
119 
120  cdev->master = master;
121 
122  cdev_init(&cdev->cdev, &eccdev_fops);
123  cdev->cdev.owner = THIS_MODULE;
124 
125  ret = cdev_add(&cdev->cdev,
126  MKDEV(MAJOR(dev_num), master->index), 1);
127  if (ret) {
128  EC_MASTER_ERR(master, "Failed to add character device!\n");
129  }
130 
131  return ret;
132 }
133 
134 /*****************************************************************************/
135 
139 {
140  cdev_del(&cdev->cdev);
141 }
142 
143 /******************************************************************************
144  * File operations
145  *****************************************************************************/
146 
149 int eccdev_open(struct inode *inode, struct file *filp)
150 {
151  ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
152  ec_cdev_priv_t *priv;
153 
154  priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
155  if (!priv) {
156  EC_MASTER_ERR(cdev->master,
157  "Failed to allocate memory for private data structure.\n");
158  return -ENOMEM;
159  }
160 
161  priv->cdev = cdev;
162  priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
163  priv->ctx.requested = 0;
164  priv->ctx.process_data = NULL;
165  priv->ctx.process_data_size = 0;
166 
167  filp->private_data = priv;
168 
169 #if DEBUG
170  EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
171 #endif
172  return 0;
173 }
174 
175 /*****************************************************************************/
176 
179 int eccdev_release(struct inode *inode, struct file *filp)
180 {
181  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
182  ec_master_t *master = priv->cdev->master;
183 
184  if (priv->ctx.requested) {
185  ecrt_release_master(master);
186  }
187 
188  if (priv->ctx.process_data) {
189  vfree(priv->ctx.process_data);
190  }
191 
192 #if DEBUG
193  EC_MASTER_DBG(master, 0, "File closed.\n");
194 #endif
195 
196  kfree(priv);
197  return 0;
198 }
199 
200 /*****************************************************************************/
201 
204 long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
205 {
206  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
207 
208 #if DEBUG
209  EC_MASTER_DBG(priv->cdev->master, 0,
210  "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
211  filp, cmd, _IOC_NR(cmd), arg);
212 #endif
213 
214  return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
215 }
216 
217 /*****************************************************************************/
218 
219 #ifndef VM_DONTDUMP
220 
222 #define VM_DONTDUMP VM_RESERVED
223 #endif
224 
233  struct file *filp,
234  struct vm_area_struct *vma
235  )
236 {
237  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
238 
239  EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
240 
241  vma->vm_ops = &eccdev_vm_ops;
242  vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
243  vma->vm_private_data = priv;
244 
245  return 0;
246 }
247 
248 /*****************************************************************************/
249 
250 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
251 
259 static int eccdev_vma_fault(
260 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
261  struct vm_area_struct *vma,
262 #endif
263  struct vm_fault *vmf
264  )
265 {
266 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
267  struct vm_area_struct *vma = vmf->vma;
268 #endif
269  unsigned long offset = vmf->pgoff << PAGE_SHIFT;
270  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
271  struct page *page;
272 
273  if (offset >= priv->ctx.process_data_size) {
274  return VM_FAULT_SIGBUS;
275  }
276 
277  page = vmalloc_to_page(priv->ctx.process_data + offset);
278  if (!page) {
279  return VM_FAULT_SIGBUS;
280  }
281 
282  get_page(page);
283  vmf->page = page;
284 
285  EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault,"
286  " offset = %lu, page = %p\n", offset, page);
287 
288  return 0;
289 }
290 
291 #else
292 
298 struct page *eccdev_vma_nopage(
299  struct vm_area_struct *vma,
301  unsigned long address,
302  int *type
303  )
304 {
305  unsigned long offset;
306  struct page *page = NOPAGE_SIGBUS;
307  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
308  ec_master_t *master = priv->cdev->master;
309 
310  offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
311 
312  if (offset >= priv->ctx.process_data_size)
313  return NOPAGE_SIGBUS;
314 
315  page = vmalloc_to_page(priv->ctx.process_data + offset);
316 
317  EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
318  " offset = %#lx, page = %p\n", address, offset, page);
319 
320  get_page(page);
321  if (type)
322  *type = VM_FAULT_MINOR;
323 
324  return page;
325 }
326 
327 #endif
328 
329 /*****************************************************************************/
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition: module.c:614
ec_master_t * master
Master owning the device.
Definition: cdev.h:50
#define VM_DONTDUMP
VM_RESERVED disappeared in 3.7.
Definition: cdev.c:222
static int eccdev_vma_fault(struct vm_fault *)
Page fault callback for a virtual memory area.
Definition: cdev.c:259
Private data structure for file handles.
Definition: cdev.c:101
struct cdev cdev
Character device.
Definition: cdev.h:51
ec_cdev_t * cdev
Character device.
Definition: cdev.c:102
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:232
struct vm_operations_struct eccdev_vm_ops
Callbacks for a virtual memory area retrieved with ecdevc_mmap().
Definition: cdev.c:89
#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:204
ec_ioctl_context_t ctx
Context.
Definition: cdev.c:103
static int eccdev_open(struct inode *, struct file *)
Called when the cdev is opened.
Definition: cdev.c:149
Vendor specific over EtherCAT protocol handler.
static int eccdev_release(struct inode *, struct file *)
Called when the cdev is closed.
Definition: cdev.c:179
void ec_cdev_clear(ec_cdev_t *cdev)
Destructor.
Definition: cdev.c:138
#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: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:79
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
Constructor.
Definition: cdev.c:112