IgH EtherCAT Master  1.5.2
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
52static int eccdev_open(struct inode *, struct file *);
53static int eccdev_release(struct inode *, struct file *);
54static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
55static 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
69static 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
75static struct page *eccdev_vma_nopage(
76 struct vm_area_struct *, unsigned long, int *);
77#endif
78
79/*****************************************************************************/
80
83static 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
93struct 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
105typedef 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
153int 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
183int 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
208long 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
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
267static 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
306struct 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/*****************************************************************************/
static int eccdev_release(struct inode *, struct file *)
Called when the cdev is closed.
Definition: cdev.c:183
static int eccdev_mmap(struct file *, struct vm_area_struct *)
Memory-map callback for the EtherCAT character device.
Definition: cdev.c:236
#define VM_DONTDUMP
VM_RESERVED disappeared in 3.7.
Definition: cdev.c:226
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
Constructor.
Definition: cdev.c:116
void ec_cdev_clear(ec_cdev_t *cdev)
Destructor.
Definition: cdev.c:142
static FAULT_RETURN_TYPE eccdev_vma_fault(struct vm_fault *)
Page fault callback for a virtual memory area.
Definition: cdev.c:267
static long eccdev_ioctl(struct file *, unsigned int, unsigned long)
Called when an ioctl() command is issued.
Definition: cdev.c:208
struct vm_operations_struct eccdev_vm_ops
Callbacks for a virtual memory area retrieved with ecdevc_mmap().
Definition: cdev.c:93
static int eccdev_open(struct inode *, struct file *)
Called when the cdev is opened.
Definition: cdev.c:153
#define DEBUG
Set to 1 to enable device operations debugging.
Definition: cdev.c:48
static struct file_operations eccdev_fops
File operation callbacks for the EtherCAT character device.
Definition: cdev.c:83
EtherCAT master character device.
Ethernet over EtherCAT (EoE)
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition: module.c:625
EtherCAT master character device IOCTL commands.
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
#define EC_MASTER_ERR(master, fmt, args...)
Convenience macro for printing master-specific errors to syslog.
Definition: master.h:85
EtherCAT slave configuration structure.
Private data structure for file handles.
Definition: cdev.c:105
ec_ioctl_context_t ctx
Context.
Definition: cdev.c:107
ec_cdev_t * cdev
Character device.
Definition: cdev.c:106
EtherCAT master character device.
Definition: cdev.h:49
struct cdev cdev
Character device.
Definition: cdev.h:51
ec_master_t * master
Master owning the device.
Definition: cdev.h:50
EtherCAT master.
Definition: master.h:194
unsigned int index
Index.
Definition: master.h:195
Vendor specific over EtherCAT protocol handler.