2 * @file cdcm-driver-gen.c
4 * @brief VME-specific operations
6 * @author Copyright (C) 2009 CERN. Yury GEORGIEVSKIY <ygeorgie@cern.ch>
8 * @date Created on 08/06/2009
10 * @section license_sec License
11 * Released under the GPL
13 #include "list_extra.h" /* for extra handy list operations */
15 #include "cdcmLynxAPI.h"
18 #include "dg/ModuleHeader.h" /* for DevInfo_t */
21 /* _GIOCTL_GET_DG_DEV_INFO ioctl argument
22 Keep syncronized with one from user space! */
23 #define DDNMSZ 32 /* device/driver name size in bytes. Same as in Lynx */
24 struct dg_module_info
{
25 int dev_major
; /* device major number */
26 int drvr_major
; /* driver major number */
27 char name
[DDNMSZ
]; /* its name */
30 static DEFINE_MUTEX(mmutex
);
33 extern cdcmStatics_t cdcmStatT
; /* CDCM statics table */
34 extern struct dldd entry_points
; /* declared in the user driver part */
35 extern char* read_info_file(char*, int*);
36 extern struct class *cdcm_class
;
39 * @brief driverGen devices
42 * major -- device major number
43 * addr -- statics table address
44 * name -- device name. String of DEVICE_NAME_FMT layout.
45 * Examples: sharedcibc_sim03.info, tric_drvr20.info, etc...
48 struct list_head list
;
55 * @brief list of device mapped memory
61 struct list_head list
;
65 static LIST_HEAD(st_list
); /* driverGen device linked list */
66 static LIST_HEAD(mmap_list
); /* mapped memory list */
69 * @brief Get device info table from driver statics table.
71 * @param di -- device info
73 * @return device info table pointer
75 static DevInfo_t
* get_dit_from_statics(void *stat
)
77 /* all dg statics tables are of standard layout */
78 struct static_layout
{
84 lo
= (struct static_layout
*)stat
;
89 * @brief Add new statics table in the list
91 * @param major -- device major number
92 * @param st -- statics table to add
93 * @param dname -- device name
95 * @return -ENOMEM - can't allocate new statics table descriptor
98 static int dg_add_st(uint major
, char *st
, char *dname
)
102 new_st
= kzalloc(sizeof(*new_st
), GFP_KERNEL
);
106 new_st
->name
= kasprintf(GFP_KERNEL
, "%s", dname
);
112 new_st
->major
= major
;
115 list_add_tail(&new_st
->list
, &st_list
);
121 * @brief Removes deviced description from the list and frees resources.
123 * @param dgd -- what to find and remove.
125 * @return zero on success or a negative number on failure
127 static int dg_rm_st(struct dgd
*dgd
)
129 struct dgd
*ptr
, *tmp
;
130 list_for_each_entry_safe(ptr
, tmp
, &st_list
, list
) {
132 list_del(&ptr
->list
); /* exclude from linked list */
134 kfree(ptr
); /* free mem */
142 * @brief Get dgd structure by statics table
144 * @param st -- statics table address
146 * @return NULL - not found
147 * @return pointer - found
150 static __attribute__((unused
)) struct dgd
* dg_get_dgd_by_st(char *st
)
153 list_for_each_entry(ptr
, &st_list
, list
) {
162 * @brief Get dgd structure by major number
164 * @param major -- device major number to find
166 * @return NULL - not found
167 * @return pointer - found
169 static struct dgd
* dg_get_dgd_by_major(uint major
)
172 list_for_each_entry(ptr
, &st_list
, list
) {
173 if (ptr
->major
== major
)
180 * @brief Get statics table address by major number
182 * @param major -- device major number to find statics table
186 * @return statics table address - if found
187 * @return NULL - not found
189 static char* dg_get_st_by_major(uint major
)
193 list_for_each_entry(ptr
, &st_list
, list
) {
194 if (ptr
->major
== major
)
202 * @brief Tune mapped VME windows info (one you can see in /proc/vme/windows)
204 * @param st -- statics table
206 * Tune info bits in order to see useful info about vmebridge mapped memory.
208 * @e mapping and @e vme_taskinfo are private && defined in vme_window.c
210 * Instead of storing pid number and task name, which in our case will always
211 * be one of the @b modinst program, will store LUN and driver name.
212 * This info is of more interest for the user.
214 * Tuning is done only in case of real driver, as simulator doesn't map any
217 static void tune_vmebridge_proc_info(void *st
)
220 DevInfo_t
*info
= get_dit_from_statics(st
);
221 AddrInfo_t
*aip
= &info
->addr1
;
222 struct vme_mapping
*vmep
;
224 struct list_head list
;
225 struct vme_mapping desc
;
226 struct vme_taskinfo
{
228 char name
[MODULE_NAME_LEN
];
232 if (info
->mlun
< 0) /* don't do for driver simulator */
235 /* pass through all possible address spaces */
236 for (i
= 0; i
< ASA
; i
++) {
237 vmep
= find_vme_mapping_from_addr(aip
->baseAddr
);
240 m
= container_of(vmep
, struct mapping
, desc
);
241 m
->client
.pid_nr
= info
->mlun
;
242 strncpy(m
->client
.name
, THIS_MODULE
->name
, MODULE_NAME_LEN
);
248 * @brief Information about all controlled devices
250 * @param arg -- user space buffer to put results.
254 * @return zero on success or a negative number on failure
256 int dg_get_dev_info(unsigned long arg
)
259 struct dg_module_info __user
*buf
= (struct dg_module_info __user
*)arg
;
260 struct dg_module_info local
;
262 list_for_each_entry(ptr
, &st_list
, list
) {
263 local
.dev_major
= ptr
->major
;
264 local
.drvr_major
= cdcmStatT
.cdcm_major
;
265 strncpy(local
.name
, ptr
->name
, sizeof(local
.name
));
266 if (copy_to_user(buf
, &local
, sizeof(local
)))
275 * @brief How many devices driver hosts.
279 * Used by driver installation/deinstallation programs basically.
281 * @return device driver amount
283 int dg_get_mod_am(void)
285 return list_capacity(&st_list
);
289 * @brief driverGen cdv_install
291 * @param name -- info table file name
292 * @param fops -- file operations
293 * @param dnm -- driver name
295 * Registers new char device and insert it in driverGen device linked list.
297 * @return major char device number - if success
298 * @return < 0 - failed
300 int dg_cdv_install(char *name
, struct file_operations
*fops
, char *dnm
)
302 unsigned int maj
; /* assigned major number */
303 char *dname
; /* device name */
304 char *st
; /* statics table */
306 char *it
; /* module-specific info table */
307 DevInfo_t
*info
= NULL
;
310 it
= read_info_file(name
, NULL
);
312 PRNT_ABS_ERR("Can't read info file.");
316 dname
= strrchr(name
, '/') + 1; /* device name is the info file name */
318 st
= entry_points
.dldd_install((void*)it
);
319 if (st
== (char*)SYSERR
) {
320 PRNT_ABS_ERR("Installation vector failed for %s device.",
326 maj
= register_chrdev(0, dname
, fops
);
328 PRNT_ABS_ERR("Can't register %s character device", dname
);
333 /* we can get info _only_ after it came from dldd_install,
334 as it is initialized there */
335 info
= get_dit_from_statics(st
);
337 /* declare ourselfs in sysfs */
338 /* NOTE! busubox mkdev will __not__ create /dev nodes with
339 * dots "." in their names */
340 for (i
= 0; i
< info
->chan
; i
++) {
341 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
342 device_create(cdcm_class
, NULL
, MKDEV(maj
, i
+1),
343 "%s.l%02d.c%02d", dnm
, abs(info
->mlun
), i
);
345 device_create(cdcm_class
, NULL
, MKDEV(maj
, i
+1), NULL
,
346 "%s.l%02d.c%02d", dnm
, abs(info
->mlun
), i
);
350 /* tune vmebridge proc info */
351 tune_vmebridge_proc_info(st
);
353 /* save statics table */
354 cc
= dg_add_st(maj
, st
, dname
);
356 PRNT_ABS_ERR("Can't add %s device (maj %d) in the"
357 " device linked list.\n", dname
, maj
);
359 /* remove ourselfs from sysfs */
360 for (i
= 0; i
< info
->chan
; i
++)
361 device_destroy(cdcm_class
, MKDEV(maj
, i
));
363 unregister_chrdev(maj
, dname
);
365 /* all OK, return allocated major dev number */
376 * @brief driverGen cdv_uninstall
378 * @param filp -- file struct pointer
379 * @param arg -- user args
381 * @return zero on success or a negative number on failure
383 int dg_cdv_uninstall(struct file
*filp
, unsigned long arg
)
385 uint major
; /* device major number */
386 struct dgd
*dgd
; /* device info */
390 if (copy_from_user(&major
, (void __user
*)arg
, sizeof(major
)))
393 dgd
= dg_get_dgd_by_major(major
);
394 if (!dgd
) /* should not happen */
397 info
= get_dit_from_statics(dgd
->addr
);
399 /* remove ourselfs from sysfs */
400 for (i
= 0; i
< info
->chan
; i
++)
401 device_destroy(cdcm_class
, MKDEV(dgd
->major
, i
+1));
403 if (entry_points
.dldd_uninstall(dgd
->addr
)) {
404 PRNT_ABS_ERR("Uninstall vector failed.");
408 /* remove from sysfs */
409 unregister_chrdev(dgd
->major
, dgd
->name
);
413 /* should not happen */
422 * @param filp -- file pointer
423 * @param buf -- kernel buffer, already allocated by CDCM
424 * @param size -- buffer size
425 * @param off -- file offset
427 * @return number of bytes actually copied, including zero - if succeed.
428 * @return SYSERR - if fails.
430 ssize_t
dg_fop_read(struct file
*filp
, char *buf
, size_t size
,
433 char *priv
; /* statics table */
435 /* not checking for NULL as it's done by open() */
436 priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
438 return entry_points
.dldd_read(priv
, (struct cdcm_file
*)filp
,
445 * @param filp -- file pointer
446 * @param buf -- kernel buffer, already allocated by CDCM
447 * @param size -- buffer size
448 * @param off -- file offset
450 * @return number of bytes actually copied, including zero - if succeed.
451 * @return SYSERR - if fails.
453 ssize_t
dg_fop_write(struct file
*filp
, const char *buf
,
454 size_t size
, loff_t
*off
)
456 char *priv
; /* statics table */
458 /* not checking for NULL as it's done by open() */
459 priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
461 return entry_points
.dldd_write(priv
, (struct cdcm_file
*)filp
,
465 unsigned int dg_fop_poll(struct file
* filp
, poll_table
* wait
)
467 char *priv
; /* statics table */
469 /* not checking for NULL as it's done by open() */
470 priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
472 return entry_points
.dldd_select(priv
, (struct cdcm_file
*)filp
,
474 (struct cdcm_sel
*)wait
);
478 long dg_fop_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
)
480 char *priv
; /* statics table */
482 /* not checking for NULL as it's done by open() */
483 priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
485 return entry_points
.dldd_ioctl(priv
, (struct cdcm_file
*)filp
,
493 * @param filp -- file pointer
496 * Mapping is based on the block index. So last mmap() parameter in user-space
497 * (offset) should be offset in pagesize, multiplied on block index, that
502 int dg_fop_mmap(struct file
*filp
, struct vm_area_struct
*vma
)
506 /* not checking for NULL as it's done by open() */
507 char *priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
508 DevInfo_t
*info
= get_dit_from_statics(priv
);
509 AddrInfo_t
*aip
= &info
->addr1
;
510 struct vme_mapping
*vmep
;
511 int asi
= vma
->vm_pgoff
; /* address space index */
513 /* will map all the size user requested */
514 unsigned long size
= vma
->vm_end
- vma
->vm_start
;
517 return -ENOSYS
; /* not allowed for simulator */
519 if (!(WITHIN_RANGE(0, asi
, ASA
-1))) {
520 PRNT_ABS_ERR("mmap() offset must be within [0 - %d]\n", ASA
-1);
525 vmep
= find_vme_mapping_from_addr(aip
[asi
].baseAddr
);
527 PRNT_ABS_ERR("Can't find mapping table for %d Address Space",
533 vma
->vm_flags
|= VM_IO
| VM_RESERVED
| VM_DONTCOPY
| VM_DONTEXPAND
;
534 vma
->vm_page_prot
= pgprot_noncached(vma
->vm_page_prot
);
536 return io_remap_pfn_range(vma
,
538 vmep
->pci_addrl
>> PAGE_SHIFT
,
549 * @param inode -- device inode
550 * @param filp -- file pointer
552 * @return zero on success or a negative number on failure
554 int dg_fop_open(struct inode
*inode
, struct file
*filp
)
556 char *priv
; /* statics table */
558 priv
= dg_get_st_by_major(imajor(filp
->f_dentry
->d_inode
));
559 if (!priv
) return -ENODEV
;
561 /* Not using filp->private_data to store priv,
562 so that Linux users can use it */
564 return entry_points
.dldd_open(priv
, inode
->i_rdev
,
565 (struct cdcm_file
*)filp
);
571 * @param inode -- device inode
572 * @param filp -- file pointer
576 * @return <ReturnValue>
578 int dg_fop_release(struct inode
*inode
, struct file
*filp
)
580 char *priv
; /* statics table */
582 /* not checking for NULL as it's done by open() */
583 priv
= dg_get_st_by_major(imajor(inode
));
585 return entry_points
.dldd_close(priv
, (struct cdcm_file
*)filp
);