vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / cdcm / cdcmDrvr.c
blobbdaa0e9bfcb2fa2eb277f5e3839846b9415a8d71
1 /**
2 * @file cdcmDrvr.c
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
11 * user Lynx driver.
12 * Many thanks to Julian Lewis and Nicolas de Metz-Noblat.
14 #include "cdcmDrvr.h"
15 #include "cdcmLynxAPI.h"
16 #include "cdcmMem.h"
17 #include "cdcmTime.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 */
40 #ifdef file
41 #undef file
42 #endif
44 /* external crap */
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),
56 .cdcm_flags = 0,
57 .cdcm_isdg = 0
60 /* LynxOs system kernel-level errno.
61 Should be set _ONLY_ by 'pseterr' function */
62 int cdcm_err = 0;
64 spinlock_t lynxos_cpu_lock = SPIN_LOCK_UNLOCKED;
66 struct file_operations cdcm_fops;
68 /**
69 * @brief Cleanup and release claimed devices. Unregister char device.
71 * @param none
73 * @return void
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,
80 di_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 */
84 kfree(dip);
87 unregister_chrdev(cdcmStatT.cdcm_major, cdcmStatT.cdcm_mn);
88 kfree(cdcmStatT.cdcm_mn);
91 /**
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)
105 void *iobuf = NULL;
107 struct cdcm_file *lynx_file = filp->private_data;
108 if (lynx_file == NULL) {
109 cdcm_err = -ENODEV;
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.");
120 return -EFAULT;
123 if ( (iobuf = cdcm_mem_alloc(size, _IOC_READ))
124 == ERR_PTR(-ENOMEM))
125 return -ENOMEM;
127 /* call user */
128 if (cdcmStatT.cdcm_isdg)
129 cdcm_err = dg_fop_read(filp, iobuf, size, off);
130 else
131 cdcm_err = entry_points.dldd_read(cdcmStatT.cdcm_st, lynx_file,
132 iobuf, size);
133 if (cdcm_err == SYSERR)
134 cdcm_err = -EAGAIN;
135 else {
136 if (__copy_to_user(buf, iobuf, size)) {
137 cdcm_mem_free(iobuf);
138 return -EFAULT;
140 *off = lynx_file->position;
143 cdcm_mem_free(iobuf);
145 return cdcm_err;
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)
162 void *iobuf = NULL;
164 struct cdcm_file *lynx_file = filp->private_data;
165 if (lynx_file == NULL) {
166 cdcm_err = -ENODEV;
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.");
177 return -EFAULT;
180 if ( (iobuf = cdcm_mem_alloc(size, _IOC_WRITE))
181 == ERR_PTR(-ENOMEM))
182 return -ENOMEM;
184 __copy_from_user(iobuf, buf, size);
186 /* call user */
187 if (cdcmStatT.cdcm_isdg)
188 cdcm_err = dg_fop_write(filp, iobuf, size, off);
189 else
190 cdcm_err = entry_points.dldd_write(cdcmStatT.cdcm_st, lynx_file,
191 iobuf, size);
192 if (cdcm_err == SYSERR)
193 cdcm_err = -EAGAIN;
194 else
195 *off = lynx_file->position;
197 cdcm_mem_free(iobuf);
199 return cdcm_err;
203 * @brief Lynx select stub. ---------> !NOT IMPLEMENTED! <--------------------
205 * @param filp -- file struct pointer
206 * @param wait --
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!
214 * @return
216 static unsigned int cdcm_fop_poll(struct file* filp, poll_table* wait)
218 int mask = POLLERR;
219 struct cdcm_sel sel;
221 struct cdcm_file *lynx_file = filp->private_data;
222 if (lynx_file == NULL) {
223 cdcm_err = -ENODEV;
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 */
234 return mask;
238 * @brief
240 * @param inode -- inode struct pointer
241 * @param file -- file struct pointer
242 * @param cmd -- IOCTL command number
243 * @param arg -- user args
245 * @return
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 */
252 switch (cmd) {
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) {
260 ret = -EFAULT;
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))) {
270 ret = -EFAULT;
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))
277 copy_from_user);
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.");
286 ret = -EAGAIN;
287 goto exit_dr_install;
288 } else
289 ret = cdcmStatT.cdcm_major;
291 exit_dr_install:
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 */
299 int rc;
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 */
306 return -ENXIO;
308 if (copy_from_user(&cid, (void __user*)arg, sizeof(cid)))
309 return -EFAULT;
311 /* hook the uza */
312 if ( (rc = entry_points.dldd_uninstall(cdcmStatT.cdcm_st))
313 != OK) {
314 PRNT_ABS_ERR("Uninstall vector failed.");
315 return -EAGAIN;
318 /* cleanup all devices */
319 cdcm_cleanup_dev();
321 return rc;
323 case _GIOCTL_GET_MOD_AM: /* how many modules */
324 if (drivergen)
325 return dg_get_mod_am();
326 else
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);
330 default:
331 return -ENOIOCTLCMD;
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.
346 * @return
348 static long process_mod_spec_ioctl(struct inode *inode, struct file *file,
349 int cmd, long arg)
351 int rc = 0; /* ret code */
352 void *iobuf = NULL;
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) {
358 cdcm_err = -ENODEV;
359 return cdcm_err; /* failure */
362 if (iodir != _IOC_NONE) { /* we should move user <-> driver data */
363 if ( (iobuf = cdcm_mem_alloc(iosz, iodir))
364 == ERR_PTR(-ENOMEM))
365 return -ENOMEM;
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);
372 return -EFAULT;
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);
384 if (rc == SYSERR) {
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);
393 return -EFAULT;
396 /* give data to the user */
397 if (__copy_to_user((char*)arg, iobuf, iosz)) {
398 cdcm_mem_free(iobuf);
399 return -EFAULT;
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,
422 unsigned long arg)
424 struct inode *inode = file->f_dentry->d_inode;
426 cdcm_err = 0; /* reset error state */
428 if (!iminor(inode))
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
441 * @param vma --
443 * @return
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);
453 return -ENOSYS;
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)
467 int dnum;
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) {
473 cdcm_err = -ENOMEM;
474 return cdcm_err;
476 filp->private_data = lynx_file; /* Save pointer to free in close */
478 if (!iminor(inode)) /* service entry point */
479 return 0;
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)
487 return -EINVAL;
488 if (filp->f_mode & FMODE_WRITE)
489 return -EINVAL;
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) {
499 cdcm_err = 0;
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) {
522 cdcm_err = -ENODEV;
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 */
532 return 0;
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;
541 return cdcm_err;
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
568 * get driver name.
570 * @param none
572 * <long-description>
574 * @return driver name
576 char* __drvrnm(void)
578 return cdcmStatT.cdcm_mn;
582 * @brief Called when module is unloading from the kernel.
584 * @param none
586 * @return void
588 static void __exit cdcm_driver_cleanup(void)
590 int cntr;
592 /* if not driverGen driver call user's uninstall entry point */
593 if (!drivergen)
594 if (entry_points.dldd_uninstall(cdcmStatT.cdcm_st) != OK)
595 PRNT_ABS_WARN("device driver did not"
596 " uninstall cleanly");
598 /* stop timers */
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);
607 cdcm_cleanup_dev();
611 * @brief Main driver initialization.
613 * @param none
615 * @return 0 - in case of success.
616 * @return non zero - otherwise.
618 static int __init cdcm_driver_init(void)
620 int err = 0;
621 if (!(cdcmStatT.cdcm_mn = kasprintf(GFP_KERNEL, "%s", cdcm_d_nm)))
622 return -ENOMEM;
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);
635 goto out_chrdev;
638 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
639 device_create(cdcm_class, NULL, MKDEV(cdcmStatT.cdcm_major, 0), "%s",
640 cdcm_d_nm);
641 #else
642 device_create(cdcm_class, NULL, MKDEV(cdcmStatT.cdcm_major, 0), NULL,
643 "%s", cdcm_d_nm);
644 #endif /* 2.6.28 */
646 cdcmStatT.cdcm_isdg = drivergen;
648 return 0;
650 out_chrdev:
651 unregister_chrdev(cdcmStatT.cdcm_major, cdcm_d_nm);
652 return err;
655 module_init(cdcm_driver_init);
656 module_exit(cdcm_driver_cleanup);