IgH EtherCAT Master  1.6.1
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 
27 /****************************************************************************/
28 
29 #include <linux/module.h>
30 #include <linux/vmalloc.h>
31 #include <linux/mm.h>
32 
33 #include "cdev.h"
34 #include "master.h"
35 #include "slave_config.h"
36 #include "voe_handler.h"
37 #include "ethernet.h"
38 #include "ioctl.h"
39 
42 #define DEBUG 0
43 
44 /****************************************************************************/
45 
46 static int eccdev_open(struct inode *, struct file *);
47 static int eccdev_release(struct inode *, struct file *);
48 static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
49 static int eccdev_mmap(struct file *, struct vm_area_struct *);
50 
55 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
56 # define FAULT_RETURN_TYPE int
57 #else
58 # define FAULT_RETURN_TYPE vm_fault_t
59 #endif
60 
62 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
63  struct vm_area_struct *,
64 #endif
65  struct vm_fault *);
66 
67 /****************************************************************************/
68 
71 static struct file_operations eccdev_fops = {
72  .owner = THIS_MODULE,
73  .open = eccdev_open,
74  .release = eccdev_release,
75  .unlocked_ioctl = eccdev_ioctl,
76  .mmap = eccdev_mmap
77 };
78 
81 struct vm_operations_struct eccdev_vm_ops = {
82  .fault = eccdev_vma_fault
83 };
84 
85 /****************************************************************************/
86 
89 typedef struct {
91  ec_ioctl_context_t ctx;
93 
94 /****************************************************************************/
95 
101  ec_cdev_t *cdev,
102  ec_master_t *master,
103  dev_t dev_num
104  )
105 {
106  int ret;
107 
108  cdev->master = master;
109 
110  cdev_init(&cdev->cdev, &eccdev_fops);
111  cdev->cdev.owner = THIS_MODULE;
112 
113  ret = cdev_add(&cdev->cdev,
114  MKDEV(MAJOR(dev_num), master->index), 1);
115  if (ret) {
116  EC_MASTER_ERR(master, "Failed to add character device!\n");
117  }
118 
119  return ret;
120 }
121 
122 /****************************************************************************/
123 
127 {
128  cdev_del(&cdev->cdev);
129 }
130 
131 /*****************************************************************************
132  * File operations
133  ****************************************************************************/
134 
137 int eccdev_open(struct inode *inode, struct file *filp)
138 {
139  ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
140  ec_cdev_priv_t *priv;
141 
142  priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
143  if (!priv) {
144  EC_MASTER_ERR(cdev->master,
145  "Failed to allocate memory for private data structure.\n");
146  return -ENOMEM;
147  }
148 
149  priv->cdev = cdev;
150  priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
151  priv->ctx.requested = 0;
152  priv->ctx.process_data = NULL;
153  priv->ctx.process_data_size = 0;
154 
155  filp->private_data = priv;
156 
157 #if DEBUG
158  EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
159 #endif
160  return 0;
161 }
162 
163 /****************************************************************************/
164 
167 int eccdev_release(struct inode *inode, struct file *filp)
168 {
169  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
170  ec_master_t *master = priv->cdev->master;
171 
172  if (priv->ctx.requested) {
173  ecrt_release_master(master);
174  }
175 
176  if (priv->ctx.process_data) {
177  vfree(priv->ctx.process_data);
178  }
179 
180 #if DEBUG
181  EC_MASTER_DBG(master, 0, "File closed.\n");
182 #endif
183 
184  kfree(priv);
185  return 0;
186 }
187 
188 /****************************************************************************/
189 
192 long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
193 {
194  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
195 
196 #if DEBUG
197  EC_MASTER_DBG(priv->cdev->master, 0,
198  "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
199  filp, cmd, _IOC_NR(cmd), arg);
200 #endif
201 
202  return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
203 }
204 
205 /****************************************************************************/
206 
207 #ifndef VM_DONTDUMP
208 
210 #define VM_DONTDUMP VM_RESERVED
211 #endif
212 
221  struct file *filp,
222  struct vm_area_struct *vma
223  )
224 {
225  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
226 
227  EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
228 
229  vma->vm_ops = &eccdev_vm_ops;
230 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
231  vm_flags_set(vma, VM_DONTDUMP);
232 #else
233  vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
234 #endif
235  vma->vm_private_data = priv;
236 
237  return 0;
238 }
239 
240 /****************************************************************************/
241 
250 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
251  struct vm_area_struct *vma,
252 #endif
253  struct vm_fault *vmf
254  )
255 {
256 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
257  struct vm_area_struct *vma = vmf->vma;
258 #endif
259  unsigned long offset = vmf->pgoff << PAGE_SHIFT;
260  ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
261  struct page *page;
262 
263  if (offset >= priv->ctx.process_data_size) {
264  return VM_FAULT_SIGBUS;
265  }
266 
267  page = vmalloc_to_page(priv->ctx.process_data + offset);
268  if (!page) {
269  return VM_FAULT_SIGBUS;
270  }
271 
272  get_page(page);
273  vmf->page = page;
274 
275  EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault,"
276  " offset = %lu, page = %p\n", offset, page);
277 
278  return 0;
279 }
280 
281 /****************************************************************************/
ec_master_t * master
Master owning the device.
Definition: cdev.h:42
#define VM_DONTDUMP
VM_RESERVED disappeared in 3.7.
Definition: cdev.c:210
Private data structure for file handles.
Definition: cdev.c:89
struct cdev cdev
Character device.
Definition: cdev.h:43
ec_cdev_t * cdev
Character device.
Definition: cdev.c:90
EtherCAT master structure.
#define EC_MASTER_DBG(master, level, fmt, args...)
Convenience macro for printing master-specific debug messages to syslog.
Definition: master.h:100
Ethernet over EtherCAT (EoE)
#define FAULT_RETURN_TYPE
This is the kernel version from which the .fault member of the vm_operations_struct is usable...
Definition: cdev.c:58
static int eccdev_mmap(struct file *, struct vm_area_struct *)
Memory-map callback for the EtherCAT character device.
Definition: cdev.c:220
struct vm_operations_struct eccdev_vm_ops
Callbacks for a virtual memory area retrieved with ecdevc_mmap().
Definition: cdev.c:81
long ec_ioctl(ec_master_t *master, ec_ioctl_context_t *ctx, unsigned int cmd, void *arg)
Called when an ioctl() command is issued.
Definition: ioctl.c:4970
#define EC_MASTER_ERR(master, fmt, args...)
Convenience macro for printing master-specific errors to syslog.
Definition: master.h:74
static long eccdev_ioctl(struct file *, unsigned int, unsigned long)
Called when an ioctl() command is issued.
Definition: cdev.c:192
ec_ioctl_context_t ctx
Context.
Definition: cdev.c:91
static int eccdev_open(struct inode *, struct file *)
Called when the cdev is opened.
Definition: cdev.c:137
Vendor specific over EtherCAT protocol handler.
static int eccdev_release(struct inode *, struct file *)
Called when the cdev is closed.
Definition: cdev.c:167
void ec_cdev_clear(ec_cdev_t *cdev)
Destructor.
Definition: cdev.c:126
#define DEBUG
Set to 1 to enable device operations debugging.
Definition: cdev.c:42
EtherCAT master character device.
Definition: cdev.h:41
static FAULT_RETURN_TYPE eccdev_vma_fault(struct vm_fault *)
Page fault callback for a virtual memory area.
Definition: cdev.c:249
EtherCAT master character device IOCTL commands.
EtherCAT slave configuration structure.
unsigned int index
Index.
Definition: master.h:188
EtherCAT master.
Definition: master.h:187
EtherCAT master character device.
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition: module.c:621
static struct file_operations eccdev_fops
File operation callbacks for the EtherCAT character device.
Definition: cdev.c:71
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
Constructor.
Definition: cdev.c:100