vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / cdcm / cdcm-driver-gen-vme.c
blob499091507b493af136052bc398bcf1032a1e57db
1 /**
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 */
14 #include "cdcmDrvr.h"
15 #include "cdcmLynxAPI.h"
16 #include "vmebus.h"
17 #define __CDCM__
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);
32 /* external crap */
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;
38 /**
39 * @brief driverGen devices
41 * list -- linked list
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...
47 struct dgd {
48 struct list_head list;
49 unsigned int major;
50 char *addr;
51 char *name;
54 /**
55 * @brief list of device mapped memory
57 * list --
58 * addr --
60 struct dgmmap {
61 struct list_head list;
62 DevInfo_t *addr;
65 static LIST_HEAD(st_list); /* driverGen device linked list */
66 static LIST_HEAD(mmap_list); /* mapped memory list */
68 /**
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 {
79 void *topology;
80 DevInfo_t *info;
81 void *usrst;
82 } *lo;
84 lo = (struct static_layout*)stat;
85 return lo->info;
88 /**
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
96 * @return 0 - success
98 static int dg_add_st(uint major, char *st, char *dname)
100 struct dgd *new_st;
102 new_st = kzalloc(sizeof(*new_st), GFP_KERNEL);
103 if (!new_st)
104 return -ENOMEM;
106 new_st->name = kasprintf(GFP_KERNEL, "%s", dname);
107 if (!new_st->name) {
108 kfree(new_st);
109 return -ENOMEM;
112 new_st->major = major;
113 new_st->addr = st;
115 list_add_tail(&new_st->list, &st_list);
117 return 0;
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) {
131 if (ptr == dgd) {
132 list_del(&ptr->list); /* exclude from linked list */
133 kfree(ptr->name);
134 kfree(ptr); /* free mem */
135 return 0;
138 return -1;
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)
152 struct dgd *ptr;
153 list_for_each_entry(ptr, &st_list, list) {
154 if (ptr->addr == st)
155 return ptr;
157 return NULL;
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)
171 struct dgd *ptr;
172 list_for_each_entry(ptr, &st_list, list) {
173 if (ptr->major == major)
174 return ptr;
176 return NULL;
180 * @brief Get statics table address by major number
182 * @param major -- device major number to find statics table
184 * <long-description>
186 * @return statics table address - if found
187 * @return NULL - not found
189 static char* dg_get_st_by_major(uint major)
191 struct dgd *ptr;
193 list_for_each_entry(ptr, &st_list, list) {
194 if (ptr->major == major)
195 return ptr->addr;
198 return NULL;
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
215 * physical memory.
217 static void tune_vmebridge_proc_info(void *st)
219 int i;
220 DevInfo_t *info = get_dit_from_statics(st);
221 AddrInfo_t *aip = &info->addr1;
222 struct vme_mapping *vmep;
223 struct mapping {
224 struct list_head list;
225 struct vme_mapping desc;
226 struct vme_taskinfo {
227 pid_t pid_nr;
228 char name[MODULE_NAME_LEN];
229 } client;
230 } *m;
232 if (info->mlun < 0) /* don't do for driver simulator */
233 return;
235 /* pass through all possible address spaces */
236 for (i = 0; i < ASA; i++) {
237 vmep = find_vme_mapping_from_addr(aip->baseAddr);
238 if (!vmep)
239 continue;
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);
243 ++aip;
248 * @brief Information about all controlled devices
250 * @param arg -- user space buffer to put results.
252 * <long-description>
254 * @return zero on success or a negative number on failure
256 int dg_get_dev_info(unsigned long arg)
258 struct dgd *ptr;
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)))
267 return -EFAULT;
268 ++buf;
271 return 0;
275 * @brief How many devices driver hosts.
277 * @param void - none
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 */
305 int cc, i;
306 char *it; /* module-specific info table */
307 DevInfo_t *info = NULL;
309 /* get info table */
310 it = read_info_file(name, NULL);
311 if (!it) {
312 PRNT_ABS_ERR("Can't read info file.");
313 return -EFAULT;
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.",
321 dname);
322 cc = -EAGAIN;
323 goto out_err;
326 maj = register_chrdev(0, dname, fops);
327 if (maj < 0) {
328 PRNT_ABS_ERR("Can't register %s character device", dname);
329 cc = maj;
330 goto out_err;
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);
344 #else
345 device_create(cdcm_class, NULL, MKDEV(maj, i+1), NULL,
346 "%s.l%02d.c%02d", dnm, abs(info->mlun), i);
347 #endif /* 2.6.28 */
350 /* tune vmebridge proc info */
351 tune_vmebridge_proc_info(st);
353 /* save statics table */
354 cc = dg_add_st(maj, st, dname);
355 if (cc) {
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);
364 } else {
365 /* all OK, return allocated major dev number */
366 cc = maj;
369 out_err:
370 kfree(it);
371 return cc;
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 */
387 DevInfo_t *info;
388 int i;
390 if (copy_from_user(&major, (void __user*)arg, sizeof(major)))
391 return -EFAULT;
393 dgd = dg_get_dgd_by_major(major);
394 if (!dgd) /* should not happen */
395 return -ENODEV;
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.");
405 return -EAGAIN;
408 /* remove from sysfs */
409 unregister_chrdev(dgd->major, dgd->name);
412 if (dg_rm_st(dgd))
413 /* should not happen */
414 return -ENODEV;
416 return 0;
420 * @brief read EP
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,
431 loff_t *off)
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,
439 buf, size);
443 * @brief write EP
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,
462 (char *)buf, size);
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,
473 0/*not vaild*/,
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,
486 cmd, (char *)arg);
491 * @brief
493 * @param filp -- file pointer
494 * @param vma --
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
498 * should be mapped.
500 * @return
502 int dg_fop_mmap(struct file *filp, struct vm_area_struct *vma)
504 int retval;
505 /* statics table */
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;
516 if (info->mlun < 0)
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);
521 retval = -EINVAL;
522 goto errout;
525 vmep = find_vme_mapping_from_addr(aip[asi].baseAddr);
526 if (!vmep) {
527 PRNT_ABS_ERR("Can't find mapping table for %d Address Space",
528 asi+1);
529 retval = -EFAULT;
530 goto errout;
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,
537 vma->vm_start,
538 vmep->pci_addrl >> PAGE_SHIFT,
539 size,
540 vma->vm_page_prot);
542 errout:
543 return retval;
547 * @brief Open file
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);
569 * @brief Close file
571 * @param inode -- device inode
572 * @param filp -- file pointer
574 * <long-description>
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);