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