00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00035
00036
00037 #include <linux/module.h>
00038 #include <linux/vmalloc.h>
00039 #include <linux/mm.h>
00040
00041 #include "cdev.h"
00042 #include "master.h"
00043 #include "slave_config.h"
00044 #include "voe_handler.h"
00045 #include "ethernet.h"
00046 #include "ioctl.h"
00047
00050 #define DEBUG 0
00051
00052
00053
00054 static int eccdev_open(struct inode *, struct file *);
00055 static int eccdev_release(struct inode *, struct file *);
00056 static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
00057 static int eccdev_mmap(struct file *, struct vm_area_struct *);
00058
00062 #define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23)
00063
00064 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
00065 static int eccdev_vma_fault(struct vm_area_struct *, struct vm_fault *);
00066 #else
00067 static struct page *eccdev_vma_nopage(
00068 struct vm_area_struct *, unsigned long, int *);
00069 #endif
00070
00071
00072
00075 static struct file_operations eccdev_fops = {
00076 .owner = THIS_MODULE,
00077 .open = eccdev_open,
00078 .release = eccdev_release,
00079 .unlocked_ioctl = eccdev_ioctl,
00080 .mmap = eccdev_mmap
00081 };
00082
00085 struct vm_operations_struct eccdev_vm_ops = {
00086 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
00087 .fault = eccdev_vma_fault
00088 #else
00089 .nopage = eccdev_vma_nopage
00090 #endif
00091 };
00092
00093
00094
00097 typedef struct {
00098 ec_cdev_t *cdev;
00099 ec_ioctl_context_t ctx;
00100 } ec_cdev_priv_t;
00101
00102
00103
00108 int ec_cdev_init(
00109 ec_cdev_t *cdev,
00110 ec_master_t *master,
00111 dev_t dev_num
00112 )
00113 {
00114 int ret;
00115
00116 cdev->master = master;
00117
00118 cdev_init(&cdev->cdev, &eccdev_fops);
00119 cdev->cdev.owner = THIS_MODULE;
00120
00121 ret = cdev_add(&cdev->cdev,
00122 MKDEV(MAJOR(dev_num), master->index), 1);
00123 if (ret) {
00124 EC_MASTER_ERR(master, "Failed to add character device!\n");
00125 }
00126
00127 return ret;
00128 }
00129
00130
00131
00134 void ec_cdev_clear(ec_cdev_t *cdev )
00135 {
00136 cdev_del(&cdev->cdev);
00137 }
00138
00139
00140
00141
00142
00145 int eccdev_open(struct inode *inode, struct file *filp)
00146 {
00147 ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
00148 ec_cdev_priv_t *priv;
00149
00150 priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
00151 if (!priv) {
00152 EC_MASTER_ERR(cdev->master,
00153 "Failed to allocate memory for private data structure.\n");
00154 return -ENOMEM;
00155 }
00156
00157 priv->cdev = cdev;
00158 priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
00159 priv->ctx.requested = 0;
00160 priv->ctx.process_data = NULL;
00161 priv->ctx.process_data_size = 0;
00162
00163 filp->private_data = priv;
00164
00165 #if DEBUG
00166 EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
00167 #endif
00168 return 0;
00169 }
00170
00171
00172
00175 int eccdev_release(struct inode *inode, struct file *filp)
00176 {
00177 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
00178 ec_master_t *master = priv->cdev->master;
00179
00180 if (priv->ctx.requested) {
00181 ecrt_release_master(master);
00182 }
00183
00184 if (priv->ctx.process_data) {
00185 vfree(priv->ctx.process_data);
00186 }
00187
00188 #if DEBUG
00189 EC_MASTER_DBG(master, 0, "File closed.\n");
00190 #endif
00191
00192 kfree(priv);
00193 return 0;
00194 }
00195
00196
00197
00200 long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
00201 {
00202 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
00203
00204 #if DEBUG
00205 EC_MASTER_DBG(priv->cdev->master, 0,
00206 "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
00207 filp, cmd, _IOC_NR(cmd), arg);
00208 #endif
00209
00210 return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
00211 }
00212
00213
00214
00215 #ifndef VM_DONTDUMP
00216
00218 #define VM_DONTDUMP VM_RESERVED
00219 #endif
00220
00228 int eccdev_mmap(
00229 struct file *filp,
00230 struct vm_area_struct *vma
00231 )
00232 {
00233 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
00234
00235 EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
00236
00237 vma->vm_ops = &eccdev_vm_ops;
00238 vma->vm_flags |= VM_DONTDUMP;
00239 vma->vm_private_data = priv;
00240
00241 return 0;
00242 }
00243
00244
00245
00246 #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
00247
00255 static int eccdev_vma_fault(
00256 struct vm_area_struct *vma,
00257 struct vm_fault *vmf
00258 )
00259 {
00260 unsigned long offset = vmf->pgoff << PAGE_SHIFT;
00261 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
00262 struct page *page;
00263
00264 if (offset >= priv->ctx.process_data_size) {
00265 return VM_FAULT_SIGBUS;
00266 }
00267
00268 page = vmalloc_to_page(priv->ctx.process_data + offset);
00269 if (!page) {
00270 return VM_FAULT_SIGBUS;
00271 }
00272
00273 get_page(page);
00274 vmf->page = page;
00275
00276 EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault, virtual_address = %p,"
00277 " offset = %lu, page = %p\n", vmf->virtual_address, offset, page);
00278
00279 return 0;
00280 }
00281
00282 #else
00283
00289 struct page *eccdev_vma_nopage(
00290 struct vm_area_struct *vma,
00292 unsigned long address,
00293 int *type
00294 )
00295 {
00296 unsigned long offset;
00297 struct page *page = NOPAGE_SIGBUS;
00298 ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
00299 ec_master_t *master = priv->cdev->master;
00300
00301 offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
00302
00303 if (offset >= priv->ctx.process_data_size)
00304 return NOPAGE_SIGBUS;
00305
00306 page = vmalloc_to_page(priv->ctx.process_data + offset);
00307
00308 EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
00309 " offset = %#lx, page = %p\n", address, offset, page);
00310
00311 get_page(page);
00312 if (type)
00313 *type = VM_FAULT_MINOR;
00314
00315 return page;
00316 }
00317
00318 #endif
00319
00320