/home/hillier_g/checkout/most4linux/most-kernel/most-netservice.c

Go to the documentation of this file.
00001 /*
00002  *  Copyright(c) Siemens AG, Muenchen, Germany, 2005, 2006, 2007
00003  *                           Bernhard Walle <bernhard.walle@gmx.de>
00004  *                           Gernot Hillier <gernot.hillier@siemens.com>
00005  *
00006  * ----------------------------------------------------------------------------
00007  * This program is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License version 2 as 
00009  * published by the Free Software Foundation;
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  * ----------------------------------------------------------------------------
00020  */
00021 
00028 #ifdef HAVE_CONFIG_H
00029 #include "config/config.h"
00030 #endif
00031 
00032 #include <linux/module.h>
00033 #include <linux/fs.h>
00034 #include <linux/spinlock.h>
00035 #include <linux/interrupt.h>
00036 #include <linux/signal.h>
00037 
00038 #include <asm/siginfo.h>
00039 #include <asm/uaccess.h>
00040 #include <asm/io.h>
00041 
00042 #include "most-pci.h"
00043 #include "most-netservice.h"
00044 #include "most-constants.h"
00045 #include "most-base.h"
00046 
00050 #define DRIVER_NAME "most-netservice"
00051 
00055 #define PR          DRIVER_NAME ": "
00056 
00057 
00061 static char *version = "$Rev: 641 $";
00062 
00063 /* forward declarations ---------------------------------------------------- */
00064 static int  most_nets_open            (struct inode *, struct file *);
00065 static int  most_nets_release         (struct inode *inode, struct file *file);
00066 static int  most_nets_ioctl           (struct inode *, struct file *, 
00067                                        unsigned int, unsigned long);
00068 static void process_sigsend_handler   (unsigned long);
00069 
00070 /* ioctls */
00071 static int  ioctl_write_register  (struct most_nets_dev *, unsigned long);
00072 static int  ioctl_read_register   (struct most_nets_dev *, unsigned long);
00073 static int  ioctl_write_regblock  (struct most_nets_dev *, unsigned long);
00074 static int  ioctl_read_regblock   (struct most_nets_dev *, unsigned long);
00075 static int  ioctl_read_int        (struct most_nets_dev *);
00076 static int  ioctl_irq_set         (struct most_nets_dev *, unsigned long);
00077 static int  ioctl_irq_reset       (struct most_nets_dev *, unsigned long);
00078 static int  ioctl_reset           (struct most_nets_dev *);
00079 
00080 
00081 /* general static data elements -------------------------------------------- */
00082 
00086 struct most_nets_dev *most_nets_devices[MOST_DEVICE_NUMBER];
00087 
00088 
00092 static struct file_operations most_nets_file_operations = {
00093     .owner   = THIS_MODULE,
00094     .open    = most_nets_open,
00095     .ioctl   = most_nets_ioctl,
00096     .release = most_nets_release
00097 };
00098 
00104 static unsigned long cards_to_send_interrupt = 0;
00105 
00110 static DECLARE_TASKLET(sigsend_tasklet, process_sigsend_handler, 0);
00111 
00117 DEFINE_NRTSIG(nrt_signal);
00118 
00119 /* functions --------------------------------------------------------------- */
00120 
00129 static void process_sigsend_handler(unsigned long data)
00130 {
00131     int i;
00132 
00133     /*
00134      * TODO: kill_proc() may be slow because it has to find out the task struct
00135      * from the PID. Because we have already the PID, we may use send_sig_info
00136      * or something like this. But send_sig_info() didn't work in my tests
00137      * (I probably did something wrong), but looking in the code of kill_proc()
00138      * should find that out. For now, it just works!
00139      */
00140     
00141     for (i = 0; i < MOST_DEVICE_NUMBER; i++) {
00142         if (test_and_clear_bit(i, &cards_to_send_interrupt)) {
00143             struct most_nets_dev* nets_dev = most_nets_devices[i];
00144             int data = 0;
00145 
00146             assert(nets_dev != NULL);
00147 
00148             if (nets_dev->task == NULL) {
00149                 continue;
00150             }
00151             if ((nets_dev->intstatus & ISMAINT) && 
00152                     (nets_dev->signo_async != 0)) {
00153                 data = MNS_AINT;
00154             }
00155             if ((nets_dev->intstatus & (ISMINT)) && nets_dev->signo != 0) {
00156                 data = MNS_INT;
00157             }
00158 
00159             assert(data != 0);
00160 
00161             /* send the signal finally */
00162             most_kill(nets_dev->signo, nets_dev->task, data);
00163 
00164             nets_dev->intstatus = 0;
00165         }
00166     }
00167 }
00168 
00176 static int most_nets_open(struct inode *inode, struct file *filp)
00177 {
00178     int                  err     = 0;
00179     struct most_nets_dev *dev;
00180 
00181     dev = container_of(inode->i_cdev, struct most_nets_dev, cdev);
00182     most_manage_usage(dev->most_dev, 1);
00183     
00184     pr_nets_debug(PR "most_nets_open called for PCI card %d\n", 
00185                   MOST_DEV_CARDNUMBER(dev->most_dev));
00186     
00187     /* ... and register it */
00188     filp->private_data = dev; 
00189 
00190     /* manage the open counter */
00191     if (!atomic_inc_and_test(&dev->open_count)) {
00192         rtnrt_info(PR "Only one device allowed to access "
00193                 "MostNetService device.\n");
00194         err = -EBUSY;
00195         goto out_dec;
00196     }
00197     
00198     return 0;
00199 
00200 out_dec:
00201     atomic_dec(&dev->open_count);
00202     most_manage_usage(dev->most_dev, -1);
00203 
00204     return err;
00205 }
00206 
00207 
00219 static int most_nets_ioctl(struct inode         *inode,
00220                            struct file          *file,
00221                            unsigned int         cmd,
00222                            unsigned long        arg)
00223 {
00224     struct most_nets_dev *dev     = (struct most_nets_dev *)file->private_data;
00225     int                  err      = 0;
00226     
00227     /*
00228      * extract the type and number bitfields, and don't decode
00229      * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
00230      */
00231     if ((_IOC_TYPE(cmd) != MOST_NETS_IOCTL_MAGIC) ||
00232             (_IOC_NR(cmd) > MOST_NETS_MAXIOCTL)) {
00233         return -ENOTTY;
00234     }
00235 
00236     /*
00237      * the direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
00238      * Type is user-orignted, while access_ok is kernel-oriented, so the concept
00239      * of read and write is reversed
00240      */
00241     if (_IOC_DIR(cmd) & _IOC_READ) {
00242         err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
00243     } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
00244         err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
00245     }
00246 
00247     /* check the error */
00248     if (err) {
00249         return -EFAULT;
00250     }
00251 
00252     /* now do the command */
00253     switch (cmd)
00254     {
00255         case MOST_NETS_WRITEREG:
00256             return ioctl_write_register(dev, arg);
00257 
00258         case MOST_NETS_READREG:
00259             return ioctl_read_register(dev, arg);
00260 
00261         case MOST_NETS_WRITEREG_BLOCK:
00262             return ioctl_write_regblock(dev, arg);
00263 
00264         case MOST_NETS_READREG_BLOCK:
00265             return ioctl_read_regblock(dev, arg);
00266 
00267         case MOST_NETS_READ_INT:
00268             return ioctl_read_int(dev);
00269 
00270         case MOST_NETS_IRQ_SET:
00271             return ioctl_irq_set(dev, arg);
00272 
00273         case MOST_NETS_IRQ_RESET:
00274             return ioctl_irq_reset(dev, arg);
00275 
00276         case MOST_NETS_RESET:
00277             return ioctl_reset(dev);
00278 
00279         default:
00280             return -ENOTTY;
00281     }
00282     
00283     return 0;
00284 }
00285 
00286 
00294 static int most_nets_release(struct inode *inode, struct file *filp)
00295 {
00296     struct most_nets_dev *dev = (struct most_nets_dev *)filp->private_data;
00297 
00298     pr_nets_debug(PR "most_nets_release called for PCI card %d\n", 
00299                   MOST_DEV_CARDNUMBER(dev->most_dev));
00300 
00301     /* manage the open counter */
00302     atomic_dec(&dev->open_count);
00303     most_manage_usage(dev->most_dev, -1);
00304 
00305     return 0;
00306 }
00307 
00308 
00316 static int ioctl_write_register(struct most_nets_dev    *dev, 
00317                                     unsigned long           ioctl_arg)
00318 {
00319     int                         ret      = 0;
00320     struct single_transfer_arg  *arg     = (void *)ioctl_arg;
00321     struct single_transfer_arg  param;
00322 
00323     /* get the argument */
00324     ret = __copy_from_user(&param, arg, sizeof(struct single_transfer_arg));
00325     if (unlikely(ret != 0)) {
00326         return -EFAULT;
00327     }
00328 
00329     pr_ioctl_debug(PR "MOST IOCTL WRITE 0x%x = 0x%x\n",
00330             param.address, param.value);
00331 
00332     ret = most_writereg8104(dev->most_dev, &param.value, 1, param.address); 
00333 
00334     return 0;
00335 }
00336 
00337 
00345 static int ioctl_read_register(struct most_nets_dev     *dev,
00346                                    unsigned long            ioctl_arg)
00347 {
00348     int                         ret      = 0;
00349     struct single_transfer_arg  *arg     = (void *)ioctl_arg;
00350     struct single_transfer_arg  param;
00351 
00352     /* get the argument */
00353     ret = __copy_from_user(&param, arg, sizeof(struct single_transfer_arg));
00354     if (unlikely(ret != 0)) {
00355         return -EFAULT;
00356     }
00357 
00358     ret = most_readreg8104(dev->most_dev, &param.value, 1, param.address);
00359     if (unlikely(ret < 0)) {
00360         return ret;
00361     }
00362     
00363     pr_ioctl_debug(PR "MOST IOCTL READ 0x%x = 0x%x\n", 
00364             param.address, param.value);
00365 
00366     ret = __copy_to_user(arg, &param, sizeof(struct single_transfer_arg));
00367     if (unlikely(ret != 0)) {
00368         return -EFAULT;
00369     }
00370 
00371     return 0;
00372 }
00373 
00383 static int ioctl_read_regblock(struct most_nets_dev    *dev, 
00384                                     unsigned long           ioctl_arg)
00385 {
00386     int                         ret      = 0;
00387     struct block_transfer_arg   *arg     = (void *)ioctl_arg;
00388     struct block_transfer_arg   param;
00389 
00390     /* get the argument */
00391     ret = __copy_from_user(&param, arg, sizeof(struct block_transfer_arg));
00392     if (unlikely(ret != 0)) {
00393         return -EFAULT;
00394     }
00395 
00396     /* now perform the read */
00397     ret = most_readreg8104(dev->most_dev, dev->buffer, param.count, 
00398             param.address);
00399     if (ret < 0) {
00400         return ret;
00401     }
00402 
00403     /* copy the read values */
00404     ret = copy_to_user(param.data, dev->buffer, ret);
00405     if (ret < 0) {
00406         return -EFAULT;
00407     }
00408     
00409     pr_ioctl_debug(PR "MOST READBLOCK 0x%x - 0x%x\n",
00410             param.address, param.address + param.count);
00411 
00412     return param.count;
00413 }
00414 
00415 
00425 static int ioctl_write_regblock(struct most_nets_dev       *dev,
00426                                      unsigned long              ioctl_arg)
00427 {
00428     int                         ret      = 0;
00429     struct block_transfer_arg   *arg     = (void *)ioctl_arg;
00430     struct block_transfer_arg   param;
00431 
00432     /* get the argument */
00433     ret = __copy_from_user(&param, arg, sizeof(struct block_transfer_arg));
00434     if (unlikely(ret != 0)) {
00435         return -EFAULT;
00436     }
00437     
00438     /* copy in the kernel buffer */
00439     ret = copy_from_user(dev->buffer, (unsigned char __user *)param.data, 
00440             param.count);
00441 
00442 #ifdef IOCTL_DEBUG
00443     {
00444         int i;
00445         for (i = 0; i < param.count; i++) {
00446             pr_ioctl_debug(PR "MOST WRITEBLOCK 0x%x = 0x%x\n",
00447                     param.address + i, *(dev->buffer + i));
00448         }
00449     }
00450 #endif
00451     
00452     ret = most_writereg8104(dev->most_dev, dev->buffer, param.count, 
00453             param.address);
00454     if (ret < 0) {
00455         return -EFAULT;
00456     }
00457     
00458     return param.count;
00459 }
00460 
00461 
00470 static int ioctl_read_int(struct most_nets_dev *dev)
00471 {
00472     int ret = 0;
00473 
00474     ret = most_readreg(dev->most_dev, MOST_PCI_INTSTATUS_REG) & ISMINT;
00475 
00476     if (ret < 0) {
00477         return -EFAULT;
00478     }
00479 
00480     pr_ioctl_debug(PR "MOST IOCTL READ_INT = %d\n", ret ? true : false);
00481 
00482     return ret ? true : false;
00483 }
00484 
00496 static int ioctl_irq_set(struct most_nets_dev   *dev, 
00497                          unsigned long          ioctl_arg)
00498 {
00499     struct        interrupt_set_arg  param;
00500     struct        interrupt_set_arg  *arg   = (void *)ioctl_arg;
00501     int           ret                       = 0;
00502 
00503     /* get and check the argument */
00504     ret = __copy_from_user(&param, arg, sizeof(struct interrupt_set_arg));
00505     if (unlikely(ret != 0)) {
00506         return -EFAULT;
00507     }
00508 
00509     /* check the argument */
00510     if ((param.signo != 0) && !is_between_excl(param.signo, 
00511                 SIGRTMIN, SIGRTMAX)) {
00512         rtnrt_err(PR "Signal number out of range: %d\n", param.signo);
00513         return -EINVAL;
00514     }
00515 
00516     pr_ioctl_debug(PR "MOST IRQ SET %d\n", param.signo);
00517 
00518     dev->task = param.sigmask == 0 ? NULL : current;
00519     dev->signo = param.signo;
00520 
00521     dev->intmask = 0;
00522     if (param.sigmask & MNS_INT) {
00523         dev->intmask |= IEMINT;
00524     }
00525     if (param.sigmask & MNS_AINT) {
00526         dev->intmask |= IEMINT;
00527     }
00528 
00529     most_intset(dev->most_dev, dev->intmask, IEMAINT | IEMINT, NULL);
00530 
00531     return 0;
00532 }
00533 
00545 static int ioctl_irq_reset(struct most_nets_dev         *dev, 
00546                            unsigned long                ioctl_arg)
00547 {
00548     int             ret = 0;
00549     unsigned char   mask;
00550 
00551     /* get and check the argument */
00552     ret = __get_user(mask, (unsigned char __user *)ioctl_arg);
00553     if (unlikely(ret != 0)) {
00554         return -EFAULT;
00555     }
00556 
00557     /* reset MOST Transceiver interrupt */
00558     ret = most_writereg8104(dev->most_dev, &mask, 1, MSGC);
00559     if (unlikely(ret != 1)) {
00560         return ret;
00561     }
00562 
00563     pr_ioctl_debug(PR "MOST IRQ RESET\n");
00564 
00565     /* enable interrupts again */
00566     most_intset(dev->most_dev, dev->intmask, IEMAINT | IEMINT, NULL);
00567 
00568     return 0;
00569 }
00570 
00571 
00577 static int ioctl_reset(struct most_nets_dev *dev)
00578 {
00579     most_reset(dev->most_dev);
00580     return 0;
00581 }
00582 
00583 
00590 static int nets_probe(struct most_dev *most_dev)
00591 {
00592     int                      number = MOST_DEV_CARDNUMBER(most_dev);
00593     struct most_nets_dev     *dev;
00594     int                      err;
00595     dev_t                    devno = MKDEV(MOST_DEV_MAJOR(most_dev),
00596                                            number + MOST_NETS_MINOR_OFFSET);
00597     char                     buffer[10];
00598 
00599     return_value_if_fails_dbg(number < MOST_DEVICE_NUMBER, -EINVAL);
00600 
00601     print_dev_t(buffer, devno);
00602     pr_nets_debug(PR "nets_probe called, registering %s", buffer);
00603 
00604     /* create a new device structure */
00605     dev = (struct most_nets_dev *)kmalloc(sizeof(struct most_nets_dev), GFP_KERNEL);
00606     if (unlikely(dev == NULL)) {
00607         printk(KERN_WARNING PR "Allocation of private data "
00608                 "structure failed\n");
00609         err = -ENOMEM;
00610         goto out;
00611     }
00612     memset(dev, 0, sizeof(struct most_nets_dev));
00613 
00614     /* initialize some members */
00615     dev->most_dev = most_dev;
00616     atomic_set(&dev->open_count, -MAX_OPEN_PROCESSES);
00617     dev->task = NULL;
00618 
00619     /* register the new character device */
00620     cdev_init(&dev->cdev, &most_nets_file_operations);
00621     dev->cdev.owner = THIS_MODULE;
00622     err = cdev_add(&dev->cdev, devno, 1);
00623     if (unlikely(err)) {
00624         printk(KERN_WARNING PR "cdev_add failed\n");
00625         goto out_free;
00626     }
00627 
00628     /* put the device in the global list of devices */
00629     most_nets_devices[number] = dev;
00630 
00631     return 0;
00632     
00633 out_free:
00634     kfree(dev);
00635 out:
00636     return err;
00637 }
00638 
00639 
00646 int nets_remove(struct most_dev *dev)
00647 {
00648     int                  number     = MOST_DEV_CARDNUMBER(dev);
00649     struct most_nets_dev *nets_dev  = most_nets_devices[number];
00650 
00651     pr_nets_debug(PR "nets_remove called, number = %d\n", number);
00652 
00653     /* remove the character device */
00654     cdev_del(&nets_dev->cdev);
00655     
00656     /* free memory and delete from the global device list */
00657     most_nets_devices[number] = NULL;
00658     kfree(nets_dev);
00659 
00660     return 0;
00661 }
00662 
00670 static inline void nrtsig_handler(rtnrt_nrtsig_t nrt_sig)
00671 {
00672     tasklet_schedule(&sigsend_tasklet);
00673 }
00674 
00685 static void nets_int_handler(struct most_dev         *dev, 
00686                                     unsigned int            intstatus)
00687 {
00688     int                  card      = MOST_DEV_CARDNUMBER(dev);
00689     struct most_nets_dev *nets_dev = most_nets_devices[card];
00690 
00691     assert(nets_dev != NULL);
00692 
00693     /* engineer that the task gets the requested interrupt */
00694     set_bit(card, &cards_to_send_interrupt);
00695 
00696     /* save the interrupt status */
00697     nets_dev->intstatus |= intstatus;
00698 
00699     /* disable MOST NetServices interrupt */
00700     most_intset(dev, 0, IEMINT | IEMINT, NULL);
00701 
00702     /* sends the signal if real-time or schedules the tasklet if NRT */
00703     rtnrt_nrtsig_action(&nrt_signal, nrtsig_handler);
00704 }
00705 
00706 
00711 static struct most_high_driver most_netservice_high_driver = {
00712     .name               = "most-netservice",
00713     .sema_list          = LIST_HEAD_INIT(most_netservice_high_driver.sema_list),
00714     .spin_list          = LIST_HEAD_INIT(most_netservice_high_driver.spin_list),
00715     .probe              = nets_probe,
00716     .remove             = nets_remove,
00717     .int_handler        = nets_int_handler,
00718     .interrupt_mask     = (IEMAINT | IEMINT)
00719 };
00720 
00721 
00727 static int __init most_nets_init(void)
00728 {
00729     int err;
00730 
00731     rtnrt_info("Loading module %s, version %s\n", DRIVER_NAME, version);
00732     
00733     /* register driver */
00734     err = most_register_high_driver(&most_netservice_high_driver);
00735     if (unlikely(err != 0)) {
00736         return err;
00737     }
00738 
00739     return rtnrt_nrtsig_init(&nrt_signal, nrtsig_handler);
00740 }
00741 
00742 
00746 static void __exit most_nets_exit(void)
00747 {
00748     most_deregister_high_driver(&most_netservice_high_driver);
00749     
00750     rtnrt_info("Unloading module %s, version %s\n", DRIVER_NAME, version);
00751 
00752     rtnrt_nrtsig_destroy(&nrt_signal);
00753 }
00754 
00755 #ifndef DOXYGEN
00756 MODULE_LICENSE("GPL");
00757 MODULE_AUTHOR("Bernhard Walle");
00758 MODULE_VERSION("$Rev: 641 $");
00759 MODULE_DESCRIPTION("Support driver for NetService library in userspace ");
00760 module_init(most_nets_init);
00761 module_exit(most_nets_exit);
00762 #endif
00763 
00764 
00765 /* vim: set ts=4 et sw=4: */

Generated on Fri Mar 9 14:48:58 2007 for MOST Linux Drivers (Linux and RTAI) by  doxygen 1.5.0