IgH EtherCAT Master  1.6.9
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
26
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
46static int eccdev_open(struct inode *, struct file *);
47static int eccdev_release(struct inode *, struct file *);
48static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
49static int eccdev_mmap(struct file *, struct vm_area_struct *);
50
54
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
71static 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
81struct vm_operations_struct eccdev_vm_ops = {
82 .fault = eccdev_vma_fault
83};
84
85/****************************************************************************/
86
89typedef 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
137int 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
167int 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
192long 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
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/****************************************************************************/
static int eccdev_release(struct inode *, struct file *)
Called when the cdev is closed.
Definition cdev.c:167
static int eccdev_mmap(struct file *, struct vm_area_struct *)
Memory-map callback for the EtherCAT character device.
Definition cdev.c:220
#define VM_DONTDUMP
VM_RESERVED disappeared in 3.7.
Definition cdev.c:210
int ec_cdev_init(ec_cdev_t *cdev, ec_master_t *master, dev_t dev_num)
Constructor.
Definition cdev.c:100
#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
void ec_cdev_clear(ec_cdev_t *cdev)
Destructor.
Definition cdev.c:126
static FAULT_RETURN_TYPE eccdev_vma_fault(struct vm_fault *)
Page fault callback for a virtual memory area.
Definition cdev.c:249
static long eccdev_ioctl(struct file *, unsigned int, unsigned long)
Called when an ioctl() command is issued.
Definition cdev.c:192
struct vm_operations_struct eccdev_vm_ops
Callbacks for a virtual memory area retrieved with ecdevc_mmap().
Definition cdev.c:81
static int eccdev_open(struct inode *, struct file *)
Called when the cdev is opened.
Definition cdev.c:137
static struct file_operations eccdev_fops
File operation callbacks for the EtherCAT character device.
Definition cdev.c:71
EtherCAT master character device.
Ethernet over EtherCAT (EoE)
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition module.c:621
struct ec_master ec_master_t
Definition ecrt.h:300
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:4972
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:100
#define EC_MASTER_ERR(master, fmt, args...)
Convenience macro for printing master-specific errors to syslog.
Definition master.h:74
EtherCAT slave configuration structure.
Private data structure for file handles.
Definition cdev.c:89
ec_ioctl_context_t ctx
Context.
Definition cdev.c:91
ec_cdev_t * cdev
Character device.
Definition cdev.c:90
EtherCAT master character device.
Definition cdev.h:41
struct cdev cdev
Character device.
Definition cdev.h:43
ec_master_t * master
Master owning the device.
Definition cdev.h:42
unsigned int index
Index.
Definition master.h:188
Vendor specific over EtherCAT protocol handler.