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 vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
247 vma->vm_private_data = priv;
248
249 return 0;
250}
251
252/*****************************************************************************/
253
254#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
255
263static FAULT_RETURN_TYPE eccdev_vma_fault(
264#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
265 struct vm_area_struct *vma,
266#endif
267 struct vm_fault *vmf
268 )
269{
270#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
271 struct vm_area_struct *vma = vmf->vma;
272#endif
273 unsigned long offset = vmf->pgoff << PAGE_SHIFT;
274 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
275 struct page *page;
276
277 if (offset >= priv->ctx.process_data_size) {
278 return VM_FAULT_SIGBUS;
279 }
280
281 page = vmalloc_to_page(priv->ctx.process_data + offset);
282 if (!page) {
283 return VM_FAULT_SIGBUS;
284 }
285
286 get_page(page);
287 vmf->page = page;
288
289 EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault,"
290 " offset = %lu, page = %p\n", offset, page);
291
292 return 0;
293}
294
295#else
296
302struct page *eccdev_vma_nopage(
303 struct vm_area_struct *vma,
305 unsigned long address,
306 int *type
307 )
308{
309 unsigned long offset;
310 struct page *page = NOPAGE_SIGBUS;
311 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
312 ec_master_t *master = priv->cdev->master;
313
314 offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
315
316 if (offset >= priv->ctx.process_data_size)
317 return NOPAGE_SIGBUS;
318
319 page = vmalloc_to_page(priv->ctx.process_data + offset);
320
321 EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
322 " offset = %#lx, page = %p\n", address, offset, page);
323
324 get_page(page);
325 if (type)
326 *type = VM_FAULT_MINOR;
327
328 return page;
329}
330
331#endif
332
333/*****************************************************************************/
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:263
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:621
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.