4 * @brief CDCM Linux driver
6 * @author Georgievskiy Yury, Alain Gagnaire. CERN.
8 * @date Created on 02/06/2006
10 * Encapsulates user-written LynxOS driver. Consider it like a wrapper of the
12 * Many thanks to Julian Lewis and Nicolas de Metz-Noblat.
15 #include "cdcmLynxAPI.h"
19 #include "config_data.h" /* for info tables data types */
20 #include "general_both.h" /* for handy macroses */
21 #include "general_ioctl.h"
22 #include "list_extra.h" /* for extra handy list operations */
23 #include "driver/libinstkernel.h" /* intall library to get/parse info tables */
24 #include "cdcm-driver-gen.h"
26 MODULE_DESCRIPTION("Common Driver Code Manager (CDCM)");
27 MODULE_AUTHOR("Yury Georgievskiy, CERN");
28 MODULE_LICENSE("GPL");
30 /* driver parameters */
31 static char cdcm_d_nm
[64] = { 0 };
32 module_param_string(dname
, cdcm_d_nm
, sizeof(cdcm_d_nm
), 0);
33 MODULE_PARM_DESC(dname
, "Driver name");
35 static int drivergen
= 0;
36 module_param(drivergen
, bool, S_IRUGO
);
37 MODULE_PARM_DESC(drivergen
, "If this is a DriverGen driver?");
39 /* to avoid struct file to be treated as struct cdcm_file */
45 extern struct dldd entry_points
; /* declared in the user driver part */
46 extern char* read_info_file(char*, int*);
49 /* general statics data that holds all information about current groups,
50 devices, threads, allocated mem, etc. managed by CDCM */
51 cdcmStatics_t cdcmStatT
= {
52 .cdcm_dev_list
= LIST_HEAD_INIT(cdcmStatT
.cdcm_dev_list
),
53 .cdcm_ipl
= (IPL_ERROR
| IPL_INFO
), /* info printout level */
54 .cdcm_thr_list_head
= LIST_HEAD_INIT(cdcmStatT
.cdcm_thr_list_head
),
55 .cdcm_sem_list_head
= LIST_HEAD_INIT(cdcmStatT
.cdcm_sem_list_head
),
60 /* LynxOs system kernel-level errno.
61 Should be set _ONLY_ by 'pseterr' function */
64 spinlock_t lynxos_cpu_lock
= SPIN_LOCK_UNLOCKED
;
66 struct file_operations cdcm_fops
;
69 * @brief Cleanup and release claimed devices. Unregister char device.
75 static void cdcm_cleanup_dev(void)
77 struct cdcm_dev_info
*dip
, *tmpp
;
79 list_for_each_entry_safe(dip
, tmpp
, &cdcmStatT
.cdcm_dev_list
,
81 /* TODO. Perform correct pci/vme cleanup */
82 list_del(&dip
->di_list
); /* exclude from linked list */
83 if (dip
->di_vme
) kfree(dip
->di_vme
); /* vme cleanup */
87 unregister_chrdev(cdcmStatT
.cdcm_major
, cdcmStatT
.cdcm_mn
);
88 kfree(cdcmStatT
.cdcm_mn
);
92 * @brief Lynx read stub.
94 * @param filp -- file struct pointer
95 * @param buf -- user space
96 * @param size -- how many bytes to read
97 * @param off -- offset
99 * @return how many bytes was read - if success.
100 * @return negative value - if error.
102 static ssize_t
cdcm_fop_read(struct file
*filp
, char __user
*buf
,
103 size_t size
, loff_t
*off
)
107 struct cdcm_file
*lynx_file
= filp
->private_data
;
108 if (lynx_file
== NULL
) {
110 return cdcm_err
; /* failure */
112 lynx_file
->position
= *off
;
114 PRNT_DBG(cdcmStatT
.cdcm_ipl
, "Read device with minor = %d",
115 MINOR(lynx_file
->dev
));
116 cdcm_err
= 0; /* reset */
118 if (!access_ok(VERIFY_WRITE
, buf
, size
)) {
119 PRNT_ABS_ERR("Can't verify user buffer for writing.");
123 if ( (iobuf
= cdcm_mem_alloc(size
, _IOC_READ
))
128 if (cdcmStatT
.cdcm_isdg
)
129 cdcm_err
= dg_fop_read(filp
, iobuf
, size
, off
);
131 cdcm_err
= entry_points
.dldd_read(cdcmStatT
.cdcm_st
, lynx_file
,
133 if (cdcm_err
== SYSERR
)
136 if (__copy_to_user(buf
, iobuf
, size
)) {
137 cdcm_mem_free(iobuf
);
140 *off
= lynx_file
->position
;
143 cdcm_mem_free(iobuf
);
149 * @brief Lynx write stub.
151 * @param filp -- file struct pointer
152 * @param buf -- user space
153 * @param size -- how many bytes to write
154 * @param off -- offset
156 * @return how many bytes was written - if success.
157 * @return negative value - if error.
159 static ssize_t
cdcm_fop_write(struct file
*filp
, const char __user
*buf
,
160 size_t size
, loff_t
*off
)
164 struct cdcm_file
*lynx_file
= filp
->private_data
;
165 if (lynx_file
== NULL
) {
167 return cdcm_err
; /* failure */
169 lynx_file
->position
= *off
;
171 PRNT_DBG(cdcmStatT
.cdcm_ipl
, "Write device with minor = %d",
172 MINOR(lynx_file
->dev
));
173 cdcm_err
= 0; /* reset */
175 if (!access_ok(VERIFY_READ
, buf
, size
)) {
176 PRNT_ABS_ERR("Can't verify user buffer for reading.");
180 if ( (iobuf
= cdcm_mem_alloc(size
, _IOC_WRITE
))
184 __copy_from_user(iobuf
, buf
, size
);
187 if (cdcmStatT
.cdcm_isdg
)
188 cdcm_err
= dg_fop_write(filp
, iobuf
, size
, off
);
190 cdcm_err
= entry_points
.dldd_write(cdcmStatT
.cdcm_st
, lynx_file
,
192 if (cdcm_err
== SYSERR
)
195 *off
= lynx_file
->position
;
197 cdcm_mem_free(iobuf
);
203 * @brief Lynx select stub. ---------> !NOT IMPLEMENTED! <--------------------
205 * @param filp -- file struct pointer
208 * I see no way to get descriptor set (in, out, except) to pass them as a third
209 * parameter (condition to monitor - SREAD, SWRITE, SEXCEPT) to the LynxOS
210 * @e select() driver entry point. We are entering @e cdcm_fop_poll() from
211 * @e do_select(), but descriptor set stays in <b>fd_set_bits *fds</b> var of
212 * @e do_select(). IMHO there is no way to hook it somehow... Fuck!
216 static unsigned int cdcm_fop_poll(struct file
* filp
, poll_table
* wait
)
221 struct cdcm_file
*lynx_file
= filp
->private_data
;
222 if (lynx_file
== NULL
) {
224 return cdcm_err
; /* failure */
227 if (cdcmStatT
.cdcm_isdg
)
228 return dg_fop_poll(filp
, wait
);
230 if (entry_points
.dldd_select(cdcmStatT
.cdcm_st
, lynx_file
,
231 SREAD
|SWRITE
|SEXCEPT
, &sel
) == OK
)
232 mask
= POLLERR
; /* for now - we are NEVER cool */
240 * @param inode -- inode struct pointer
241 * @param file -- file struct pointer
242 * @param cmd -- IOCTL command number
243 * @param arg -- user args
247 static long process_cdcm_srv_ioctl(struct inode
*inode
, struct file
*file
,
248 unsigned int cmd
, unsigned long arg
)
250 int ret
= 0; /* return code. OK by default */
253 case _GIOCTL_CDV_INSTALL
: /* cdv_install() */
255 char itp
[128] = { 0 }; /* info table file name */
256 ulong
*addr
= NULL
; /* user space info table address */
258 /* get info file path */
259 if (strncpy_from_user(itp
, (char*)arg
, 128) < 0) {
261 PRNT_ABS_ERR("Can't get info file path from user.");
262 goto exit_dr_install
;
265 if (cdcmStatT
.cdcm_isdg
) /* drivergen will do his job */
266 return dg_cdv_install(itp
, &cdcm_fops
, cdcm_d_nm
);
268 /* get user-space address to copy info table from */
269 if (!(addr
= (ulong
*)read_info_file(itp
, NULL
))) {
271 PRNT_ABS_ERR("Can't read info file.");
272 goto exit_dr_install
;
275 /* set copy method */
276 InsLibSetCopyRoutine((void(*)(void*, const void*, int n
))
279 /* infotable should be copied by the user */
281 /* hook the uza, call install entry point,
282 save user statics table */
283 cdcmStatT
.cdcm_st
= entry_points
.dldd_install((void*)addr
);
284 if (cdcmStatT
.cdcm_st
== (char*)SYSERR
) {
285 PRNT_ABS_ERR("Install vector failed.");
287 goto exit_dr_install
;
289 ret
= cdcmStatT
.cdcm_major
;
292 if (addr
) sysfree((char*)addr
, 0/*not used*/);
294 return ret
; /* return either major number or error */
296 case _GIOCTL_CDV_UNINSTALL
: /* cdv_uninstall() */
298 int cid
= 0; /* device ID */
301 if (cdcmStatT
.cdcm_isdg
) /* drivergen will do his job */
302 return dg_cdv_uninstall(file
, arg
);
304 if (list_empty(&cdcmStatT
.cdcm_dev_list
))
305 /* no device installed */
308 if (copy_from_user(&cid
, (void __user
*)arg
, sizeof(cid
)))
312 if ( (rc
= entry_points
.dldd_uninstall(cdcmStatT
.cdcm_st
))
314 PRNT_ABS_ERR("Uninstall vector failed.");
318 /* cleanup all devices */
323 case _GIOCTL_GET_MOD_AM
: /* how many modules */
325 return dg_get_mod_am();
327 return list_capacity(&cdcmStatT
.cdcm_dev_list
);
328 case _GIOCTL_GET_DG_DEV_INFO
: /* driver gen info */
329 return dg_get_dev_info(arg
);
336 * @brief Module-specific Ioctl routine.
338 * @param inode -- inode struct pointer
339 * @param file -- file struct pointer
340 * @param cmd -- IOCTL command number
341 * @param arg -- user args
343 * Called in case if major number is not corresponding to the service one.
344 * I.e. this device node was created by the user and not by the CDCM.
348 static long process_mod_spec_ioctl(struct inode
*inode
, struct file
*file
,
351 int rc
= 0; /* ret code */
353 int iodir
= _IOC_DIR(cmd
);
354 int iosz
= _IOC_SIZE(cmd
);
356 struct cdcm_file
*lynx_file
= file
->private_data
;
357 if (lynx_file
== NULL
) {
359 return cdcm_err
; /* failure */
362 if (iodir
!= _IOC_NONE
) { /* we should move user <-> driver data */
363 if ( (iobuf
= cdcm_mem_alloc(iosz
, iodir
))
368 if (iodir
& _IOC_WRITE
) {
369 if (!access_ok(VERIFY_READ
, (void __user
*)arg
, iosz
)) {
370 PRNT_ABS_ERR("Can't verify user buffer for reading.");
371 cdcm_mem_free(iobuf
);
374 /* take data from user */
375 __copy_from_user(iobuf
, (char*)arg
, iosz
);
378 /* Carefull with the type correspondance.
379 int in Lynx <-> unsigned int in Linux */
381 /* hook the user ioctl */
382 rc
= entry_points
.dldd_ioctl(cdcmStatT
.cdcm_st
, lynx_file
, cmd
,
383 (iodir
== _IOC_NONE
) ? NULL
: iobuf
);
385 cdcm_mem_free(iobuf
);
386 return -(cdcm_err
); /* error is set by the user */
389 if (iodir
& _IOC_READ
) {
390 if (!access_ok(VERIFY_WRITE
, (void __user
*)arg
, iosz
)) {
391 PRNT_ABS_ERR("Can't verify user buffer for writing.");
392 cdcm_mem_free(iobuf
);
396 /* give data to the user */
397 if (__copy_to_user((char*)arg
, iobuf
, iosz
)) {
398 cdcm_mem_free(iobuf
);
403 cdcm_mem_free(iobuf
);
404 return rc
; /* we cool, return user-set return code */
408 * @brief CDCM ioctl routine.
410 * @param file -- file struct pointer
411 * @param cmd -- IOCTL command number
412 * @param arg -- user args
414 * Service ioctl (with CDCM_SRVIO_ prefix) normally are called by the
415 * installation program to get all needed information. All the rest considered
416 * to be user part and is passed downstream.
418 * @return 0 or some positve value - in case of success.
419 * @return -EINVAL, -ENOMEM, -EIO, etc... - if fails.
421 static long cdcm_fop_ioctl(struct file
*file
, unsigned int cmd
,
424 struct inode
*inode
= file
->f_dentry
->d_inode
;
426 cdcm_err
= 0; /* reset error state */
429 return process_cdcm_srv_ioctl(inode
, file
, cmd
, arg
);
431 if (cdcmStatT
.cdcm_isdg
) /* drivergen */
432 return dg_fop_ioctl(file
, cmd
, arg
);
434 return process_mod_spec_ioctl(inode
, file
, cmd
, arg
);
438 * @brief Device I/O memory mapping to the user space
440 * @param file -- file struct pointer
445 static int cdcm_fop_mmap(struct file
*file
, struct vm_area_struct
*vma
)
447 if (cdcmStatT
.cdcm_isdg
)
448 return dg_fop_mmap(file
, vma
);
450 //vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
451 //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
457 * @brief Open entry point.
459 * @param inode -- inode pointer
460 * @param filp -- file pointer
462 * @return 0 - if success.
463 * @return < 0 - if fails.
465 static int cdcm_fop_open(struct inode
*inode
, struct file
*filp
)
469 /* Allocate lynx_file because it contains a "buffer" pointer */
471 struct cdcm_file
*lynx_file
= kmalloc(sizeof(struct cdcm_file
), GFP_KERNEL
);
472 if (lynx_file
== NULL
) {
476 filp
->private_data
= lynx_file
; /* Save pointer to free in close */
478 if (!iminor(inode
)) /* service entry point */
481 if (cdcmStatT
.cdcm_isdg
) /* drivergen */
482 return dg_fop_open(inode
, filp
);
484 /* enforce read-only access to this chrdev */
486 if ((filp->f_mode & FMODE_READ) == 0)
488 if (filp->f_mode & FMODE_WRITE)
492 /* fill in Lynxos file */
493 dnum
= (int) inode
->i_rdev
;
494 lynx_file
->dev
= dnum
;
495 lynx_file
->access_mode
= (filp
->f_flags
& O_ACCMODE
);
496 lynx_file
->buffer
= NULL
;
498 if (entry_points
.dldd_open(cdcmStatT
.cdcm_st
, dnum
, lynx_file
) == OK
) {
500 return cdcm_err
; /* success */
503 kfree((void *) filp
->private_data
);
504 filp
->private_data
= 0;
505 cdcm_err
= -ENODEV
; /* TODO. What val to return??? */
506 return cdcm_err
; /* failure */
510 * @brief Close entry point.
512 * @param inode -- inode pointer
513 * @param filp -- file pointer
515 * @return 0 - if success.
516 * @return < 0 - if fails.
518 static int cdcm_fop_release(struct inode
*inode
, struct file
*filp
)
520 struct cdcm_file
*lynx_file
= filp
->private_data
;
521 if (lynx_file
== NULL
) {
523 return cdcm_err
; /* failure */
526 if (cdcmStatT
.cdcm_isdg
)
527 return dg_fop_release(inode
, filp
);
529 cdcm_err
= 0; /* reset */
531 if (!iminor(inode
)) /* service entry point. Don't call uep */
534 /* fill in Lynxos file */
536 if (entry_points
.dldd_close(cdcmStatT
.cdcm_st
, lynx_file
) != OK
)
537 cdcm_err
= -ENODEV
; /* TODO. What val to return??? */
539 kfree((void *) filp
->private_data
);
540 filp
->private_data
= 0;
544 static struct fasync_struct
*cdcm_async_queue
;
546 static int cdcm_fasync(int fd
, struct file
*filp
, int on
)
548 return fasync_helper(fd
, filp
, on
, &cdcm_async_queue
);
551 /* CDCM entry points */
552 struct file_operations cdcm_fops
= {
553 .owner
= THIS_MODULE
,
554 .read
= cdcm_fop_read
, /* read */
555 .write
= cdcm_fop_write
, /* write */
556 .poll
= cdcm_fop_poll
, /* select - NOT COMPATIBLE! */
557 .unlocked_ioctl
= cdcm_fop_ioctl
, /* ioctl */
558 .mmap
= cdcm_fop_mmap
, /* mmap */
559 .open
= cdcm_fop_open
, /* open */
560 .release
= cdcm_fop_release
, /* close */
561 .fasync
= cdcm_fasync
/* linux-only */
564 struct class *cdcm_class
;
567 * @brief Debug printout mechanism (drvr_dgb_prnt.h) needs this function to
574 * @return driver name
578 return cdcmStatT
.cdcm_mn
;
582 * @brief Called when module is unloading from the kernel.
588 static void __exit
cdcm_driver_cleanup(void)
592 /* if not driverGen driver call user's uninstall entry point */
594 if (entry_points
.dldd_uninstall(cdcmStatT
.cdcm_st
) != OK
)
595 PRNT_ABS_WARN("device driver did not"
596 " uninstall cleanly");
599 for (cntr
= 0; cntr
< MAX_CDCM_TIMERS
; cntr
++)
600 if (cdcmStatT
.cdcm_timer
[cntr
].ct_on
)
601 del_timer_sync(&cdcmStatT
.cdcm_timer
[cntr
].ct_timer
);
603 cdcm_sema_cleanup_all(); /* cleanup semaphores */
605 device_destroy(cdcm_class
, MKDEV(cdcmStatT
.cdcm_major
, 0));
606 class_destroy(cdcm_class
);
611 * @brief Main driver initialization.
615 * @return 0 - in case of success.
616 * @return non zero - otherwise.
618 static int __init
cdcm_driver_init(void)
621 if (!(cdcmStatT
.cdcm_mn
= kasprintf(GFP_KERNEL
, "%s", cdcm_d_nm
)))
624 /* register character device */
625 cdcmStatT
.cdcm_major
= register_chrdev(0, cdcm_d_nm
, &cdcm_fops
);
626 if (cdcmStatT
.cdcm_major
< 0) {
627 PRNT_ABS_ERR("Can't register character device");
628 return cdcmStatT
.cdcm_major
;
631 /* declare ourself in sysfs */
632 cdcm_class
= class_create(THIS_MODULE
, cdcm_d_nm
);
633 if (IS_ERR(cdcm_class
)) {
634 err
= PTR_ERR(cdcm_class
);
638 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
639 device_create(cdcm_class
, NULL
, MKDEV(cdcmStatT
.cdcm_major
, 0), "%s",
642 device_create(cdcm_class
, NULL
, MKDEV(cdcmStatT
.cdcm_major
, 0), NULL
,
646 cdcmStatT
.cdcm_isdg
= drivergen
;
651 unregister_chrdev(cdcmStatT
.cdcm_major
, cdcm_d_nm
);
655 module_init(cdcm_driver_init
);
656 module_exit(cdcm_driver_cleanup
);