reference count for device opens added (helps while asynchronous remove
authorIng. Jan Kriz <devel@jojen.net>
Sun, 17 Apr 2011 17:39:36 +0000 (19:39 +0200)
committerIng. Jan Kriz <devel@jojen.net>
Sun, 17 Apr 2011 17:39:36 +0000 (19:39 +0200)
of the USB device occurs)

lincan/include/can_sysdep.h
lincan/include/main.h
lincan/include/usbcan.h
lincan/src/close.c
lincan/src/hcan2.c
lincan/src/main.c
lincan/src/open.c
lincan/src/setup.c
lincan/src/usbcan.c

index ff06946..5ade341 100644 (file)
@@ -56,6 +56,9 @@
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/time.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       #include <linux/kref.h>
+#endif
 #include <asm/errno.h>
 
 #include <asm/io.h>
index 01f8739..c82935b 100644 (file)
@@ -113,6 +113,10 @@ struct candevice_t {
 
        struct canhardware_t *hosthardware_p;
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       struct kref refcount;
+#endif
+       
        union {
                void *anydev;
            #ifdef CAN_ENABLE_PCI_SUPPORT
@@ -326,6 +330,11 @@ struct hwspecops_t {
        int (*program_irq)(struct candevice_t *candev);
        void (*write_register)(unsigned data, can_ioptr_t address);
        unsigned (*read_register)(can_ioptr_t address);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       void (*release_device)(struct kref *refcount);
+#endif
+       
 };
 
 /**
@@ -539,4 +548,5 @@ extern int can_rtl_priority;
 #endif /*CAN_WITH_RTL*/
 
 extern struct candevice_t* register_hotplug_dev(const char *hwname,int (*chipdataregfnc)(struct canchip_t *chip,void *data),void *devdata);
+extern void deregister_hotplug_dev(struct candevice_t *dev);
 extern void cleanup_hotplug_dev(struct candevice_t *dev);
index f29f9b8..35136da 100644 (file)
@@ -188,6 +188,7 @@ struct usbcan_devs {
        struct usbcan_usb **devs;
        int     count;
        struct candevice_t *candev;
+       struct usb_device *udev;
 };
 
 
index a69247c..157acfa 100644 (file)
@@ -45,6 +45,7 @@
 int can_close(struct inode *inode, struct file *file)
 {
        struct canuser_t *canuser = (struct canuser_t*)(file->private_data);
+       struct canchip_t *chip;
        struct canque_ends_t *qends;
        struct msgobj_t *obj;
        can_spin_irqflags_t iflags;
@@ -53,6 +54,10 @@ int can_close(struct inode *inode, struct file *file)
                CANMSG("can_close: bad canuser magic\n");
                return -ENODEV;
        }
+       if ((chip=objects_p[MINOR_NR]->hostchip) == NULL) {
+               CANMSG("There is no hardware support for the device file with minor nr.: %d\n",MINOR_NR);
+               return -ENODEV;
+       }
        
        obj = canuser->msgobj;
        qends = canuser->qends;
@@ -80,5 +85,10 @@ int can_close(struct inode *inode, struct file *file)
     #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,50))
        MOD_DEC_USE_COUNT;
     #endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       if (chip->hostdevice->hwspecops->release_device)
+               kref_put(&chip->hostdevice->refcount,chip->hostdevice->hwspecops->release_device);
+#endif
        return 0;
 }
index 23d15ab..f81ae58 100644 (file)
@@ -687,8 +687,10 @@ int hcan2_filtch_rq(struct canchip_t *chip, struct msgobj_t * obj)
 
        /* HCAN2 uses oposite logic for LAFM: 1-ignore bit, 0-use bit as mask */
 
+#if myDEBUG
        DEBUGMSG("CNT: %d ID: 0x%08x MASK: 0x%08x\n", num, (uint32_t) (filter.id) & 0x1fffffff, (uint32_t) (~filter.mask) & 0x1fffffff);
-
+#endif
+       
        if (filter.flags & MSG_EXT)             /* Extended ID */
                return hcan2_extended_mask(chip, filter.id, ~filter.mask);
        else                                    /* Standard ID */
index 8018b58..367c98d 100644 (file)
@@ -464,8 +464,10 @@ struct candevice_t* register_hotplug_dev(const char *hwname,int (*chipdataregfnc
        }
        hw[board]=brp->boardtype;
 
-       if (init_new_hw_struct(board))
+       if (init_new_hw_struct(board)){
+               CANMSG("HW struct creation failed.\n");
                return NULL;
+       }
 
        #ifdef CAN_DEBUG
                list_hw();
@@ -510,7 +512,7 @@ struct candevice_t* register_hotplug_dev(const char *hwname,int (*chipdataregfnc
                        CANMSG("Error to program board interrupt\n");
                        goto interrupt_error;
                }
-
+       CANMSG("Registering /proc entry\n");
 #ifdef CONFIG_PROC_FS
        if (can_init_procentry(board))
                goto proc_error;
@@ -592,14 +594,14 @@ struct candevice_t* register_hotplug_dev(const char *hwname,int (*chipdataregfnc
                return NULL;
 }
 
-void cleanup_hotplug_dev(struct candevice_t *dev)
+void deregister_hotplug_dev(struct candevice_t *dev)
 {
        int i=0;
        int dev_minor;
 
        if (!dev)
                return;
-       DEBUGMSG("Cleaning up hotplug device.\n");
+       DEBUGMSG("Deregistering hotplug device.\n");
 
 #ifdef CONFIG_PROC_FS
        if (can_delete_procentry(dev))
@@ -629,6 +631,18 @@ void cleanup_hotplug_dev(struct candevice_t *dev)
        }
 #endif
 
+}
+
+
+void cleanup_hotplug_dev(struct candevice_t *dev)
+{
+       int i=0;
+       int dev_minor;
+
+       if (!dev)
+               return;
+       DEBUGMSG("Cleaning up hotplug device.\n");
+
        for(i=0;i<MAX_TOT_CHIPS;i++){
                if(!chips_p[i]) continue;
                if(chips_p[i]->hostdevice != dev) continue;
index 535c2c3..f0c3fd2 100644 (file)
@@ -82,6 +82,10 @@ int can_open(struct inode *inode, struct file *file)
                        CANMSG("Error initializing chip for receiving\n");
        }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       if (chip->hostdevice->hwspecops->release_device)
+               kref_get(&chip->hostdevice->refcount);
+#endif
 
        canuser = (struct canuser_t *)kmalloc(sizeof(struct canuser_t), GFP_KERNEL);
        if(canuser == NULL) goto no_canuser;
index b051d04..e36d8ef 100644 (file)
@@ -255,6 +255,10 @@ int init_device_struct(int card, int *chan_param_idx_p, int *irq_param_idx_p)
        candev->dev_base_addr=io[card];
        clock=clockfreq[card];
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       kref_init(&candev->refcount);
+#endif
+
        candev->hwspecops=(struct hwspecops_t *)can_checked_malloc(sizeof(struct hwspecops_t));
        if (candev->hwspecops==NULL)
                goto error_nomem;
index a5e938a..19b1dc6 100644 (file)
 #include "../include/devcommon.h"
 #include "../include/setup.h"
 #include "../include/usbcan.h"
-
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
  #include <linux/freezer.h>
 #endif
+#include <linux/smp_lock.h>
 
 static int usbcan_probe(struct usb_interface *interface, const struct usb_device_id *id);
 static void usbcan_disconnect(struct usb_interface *interface);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+void release_device(struct kref *refcount);
+#else
+void release_device(struct candevice_t *candev);
+#endif
 
 volatile int usbcan_chip_count=0;
 
@@ -160,7 +165,6 @@ int usbcan_program_irq(struct candevice_t *candev)
        return 0;
 }
 
-/* !!! Don't change this function !!! */
 int usbcan_register(struct hwspecops_t *hwspecops)
 {
        hwspecops->request_io = usbcan_request_io;
@@ -172,6 +176,9 @@ int usbcan_register(struct hwspecops_t *hwspecops)
        hwspecops->write_register = NULL;
        hwspecops->read_register = NULL;
        hwspecops->program_irq = usbcan_program_irq;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       hwspecops->release_device = release_device;
+#endif
        return 0;
 }
 
@@ -621,8 +628,8 @@ int usbcan_release_chip(struct canchip_t *chip)
 
        /* terminate the kernel thread */
        set_bit(USBCAN_TERMINATE,&dev->flags);
-       wake_up_process(dev->comthread);
-//     can_kthread_stop(dev->comthread);
+//     wake_up_process(dev->comthread);
+       can_kthread_stop(dev->comthread);
 
        return 0;
 }
@@ -1078,7 +1085,7 @@ static void usbcan_tx_callback(struct urb *urb)
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
                CANMSG("%s > Urb shutting down with status: %d\n", __FUNCTION__, urb->status);
-               set_bit(USBCAN_TERMINATE,&m->dev->flags);
+//             set_bit(USBCAN_TERMINATE,&m->dev->flags);
                set_bit(USBCAN_MESSAGE_TERMINATE,&m->flags);
                clear_bit(USBCAN_MESSAGE_URB_PENDING,&m->flags);
                return;
@@ -1129,7 +1136,7 @@ static void usbcan_rx_callback(struct urb *urb)
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
                CANMSG("%s > Urb shutting down with status: %d\n", __FUNCTION__, urb->status);
-               set_bit(USBCAN_TERMINATE,&m->dev->flags);
+//             set_bit(USBCAN_TERMINATE,&m->dev->flags);
                set_bit(USBCAN_MESSAGE_TERMINATE,&m->flags);
                clear_bit(USBCAN_MESSAGE_URB_PENDING,&m->flags);
                return;
@@ -1207,7 +1214,6 @@ int usbcan_kthread(void *data)
                goto error;
        obj=dev->chip->msgobj[0];
 
-
        INIT_LIST_HEAD(&dev->rx_pend_list);
        INIT_LIST_HEAD(&dev->rx_ready_list);
        INIT_LIST_HEAD(&dev->tx_idle_list);
@@ -1295,30 +1301,13 @@ int usbcan_kthread(void *data)
                the flags are visible on all CPUs. */
                mb();
                /* fall asleep */
-               if (!(can_kthread_should_stop() || test_bit(USBCAN_TERMINATE,&dev->flags))){
-                       if (usbcan_sleep_thread(dev)<0)
-                               break;
-/*                     wait_event_interruptible(dev->queue,
-                               can_kthread_should_stop() ||
-                               test_bit(USBCAN_DATA_OK,&dev->flags) ||
-                               test_bit(USBCAN_TX_PENDING,&dev->flags) ||
-                               test_bit(USBCAN_TERMINATE,&dev->flags) ||
-                               test_bit(USBCAN_ERROR,&dev->flags)
-                       );*/
-               }
-               /* We need to do a memory barrier here to be sure that
-               the flags are visible on all CPUs. */
-               mb();
-
-               /* here we are back from sleep because we caught a signal. */
-               if (can_kthread_should_stop()){
-                       /* we received a request to terminate ourself */
+               if (!can_kthread_should_stop() && !test_bit(USBCAN_TERMINATE,&dev->flags) && (usbcan_sleep_thread(dev)<0)){
                        break;
                }
+               /* We need to do a memory barrier here to be sure that the flags are visible on all CPUs. */
+               mb();
 
-               /* here we are back from sleep because we caught a signal. */
-               if (test_bit(USBCAN_TERMINATE,&dev->flags)){
-                       /* we received a request to terminate ourself */
+               if (can_kthread_should_stop() || test_bit(USBCAN_TERMINATE,&dev->flags)){
                        break;
                }
 
@@ -1345,11 +1334,8 @@ int usbcan_kthread(void *data)
 
        set_bit(USBCAN_TERMINATE,&dev->flags);
 exit:
-  /* here we go only in case of termination of the thread */
 
        usbcan_kthread_free_urbs(dev);
-
-
        clear_bit(USBCAN_THREAD_RUNNING,&dev->flags);
 
        CANMSG ("usbcan thread finished!\n");
@@ -1381,16 +1367,15 @@ static int usbcan_probe(struct usb_interface *interface, const struct usb_device
 
        usbdevs = (struct usbcan_devs *) can_checked_malloc(sizeof(struct usbcan_devs));
        if (!usbdevs) {
-               CANMSG("Out of memory");
-               goto error;
+               goto noalloc;
        }
        memset(usbdevs, 0, sizeof(struct usbcan_devs));
 
-       usbdevs->count=usbcan_chip_count;
+       usbdevs->count = usbcan_chip_count;
+       usbdevs->udev = interface_to_usbdev(interface);
 
        usbdevs->devs = (struct usbcan_usb **) can_checked_malloc(usbcan_chip_count * sizeof(struct usbcan_usb *));
        if (!usbdevs->devs) {
-               CANMSG("Out of memory");
                goto error;
        }
        memset(usbdevs->devs, 0, usbcan_chip_count * sizeof(struct usbcan_usb *));
@@ -1402,16 +1387,15 @@ static int usbcan_probe(struct usb_interface *interface, const struct usb_device
                /* allocate memory for our device state and initialize it */
                usbdevs->devs[j] = (struct usbcan_usb *) can_checked_malloc(sizeof(struct usbcan_usb));
                if (!usbdevs->devs[j]) {
-                       CANMSG("Out of memory");
                        goto error;
                }
                memset(usbdevs->devs[j], 0, sizeof(struct usbcan_usb));
                dev=usbdevs->devs[j];
-               spin_lock_init(&usbdevs->devs[j]->list_lock);
+               spin_lock_init(&dev->list_lock);
 
                mutex_init(&dev->io_mutex);
                init_waitqueue_head(&dev->queue);
-               dev->udev = interface_to_usbdev(interface);
+               dev->udev = usbdevs->udev;
                dev->interface = interface;
 
                /* set up the endpoint information */
@@ -1457,24 +1441,28 @@ static int usbcan_probe(struct usb_interface *interface, const struct usb_device
                        goto error;
                }
        }
+
+       usb_get_dev(usbdevs->udev);
+       
        /* save our data pointer in this interface device */
        usb_set_intfdata(interface, usbdevs);
 
-       if (!(usbdevs->candev=register_hotplug_dev("usbcan", usbcan_register_devs,(void *) usbdevs)))
+       usbdevs->candev=register_hotplug_dev("usbcan", usbcan_register_devs,(void *) usbdevs);
+       if (!(usbdevs->candev)){
+               CANMSG("register_hotplug_dev() failed\n");
                goto register_error;
+       }
 
        /* let the user know what node this device is now attached to */
        CANMSG("USBCAN device now attached\n");
        return 0;
 
 register_error:
-       cleanup_hotplug_dev(usbdevs->candev);
+//     cleanup_hotplug_dev(usbdevs->candev);
+       usb_put_dev(usbdevs->udev);
 error:
        if (usbdevs){
                if (usbdevs->devs){
-                       if (usbdevs->devs[0]){
-                               usb_put_dev(usbdevs->devs[0]->udev);
-                       }
                        for (j=0;j<usbdevs->count;j++){
                                if (!usbdevs->devs[j])  continue;
 
@@ -1493,45 +1481,31 @@ noalloc:
        return retval;
 }
 
-// Physically disconnected device
-static void usbcan_disconnect(struct usb_interface *interface)
-{
-       struct usbcan_devs *usbdevs;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+void release_device(struct kref *refcount){
+       struct candevice_t *candev = container_of(refcount,struct candevice_t,refcount);
+#else
+void release_device(struct candevice_t *candev){
+#endif
+       struct usbcan_devs *usbdevs = (struct usbcan_devs *)candev->sysdevptr.anydev;
        int j;
-       usbdevs = usb_get_intfdata(interface);
-       if (usbdevs==NULL){
-               CANMSG("USBCAN device seems to be removed\n");
+       
+       if (!usbdevs)
                return;
-       }
-       usb_set_intfdata(interface, NULL);
-
-       if (usbdevs->devs){
-               usb_put_dev((*usbdevs->devs)->udev);
-       }
+       
        cleanup_hotplug_dev(usbdevs->candev);
+
        if (usbdevs->devs){
+               /* Finally, release all structures in USB subsystem */
+               if (!usbdevs->udev)
+                       panic("udev is already null on device release");
+               usb_put_dev(usbdevs->udev);
+
                for (j=0;j<usbdevs->count;j++){
                        if (!usbdevs->devs[j])  continue;
 
-                       /* prevent more I/O from starting */
-                       mutex_lock(&usbdevs->devs[j]->io_mutex);
-                       usbdevs->devs[j]->interface = NULL;
-                       mutex_unlock(&usbdevs->devs[j]->io_mutex);
-
-                       while (test_bit(USBCAN_THREAD_RUNNING,&usbdevs->devs[j]->flags))
-                       {
-                               CANMSG("USBCAN thread has not stopped, trying to wake...\n");
-                               set_bit(USBCAN_TERMINATE,&usbdevs->devs[j]->flags);
-                               wake_up_process(usbdevs->devs[j]->comthread);
-                               schedule();
-//                     can_kthread_stop(dev->comthread);
-                       }
-
                        if (usbdevs->devs[j]->bulk_in_buffer)
                                can_checked_free(usbdevs->devs[j]->bulk_in_buffer);
-                       // if (usbdevs->devs[j]->chip){
-                       //      usbdevs->devs[j]->chip->chip_data=NULL;
-                       // }
                        can_checked_free(usbdevs->devs[j]);
                        usbdevs->devs[j]=NULL;
                }
@@ -1542,6 +1516,40 @@ static void usbcan_disconnect(struct usb_interface *interface)
        CANMSG("USBCAN now disconnected\n");
 }
 
+// Physically disconnected device
+static void usbcan_disconnect(struct usb_interface *interface)
+{
+       struct usbcan_devs *usbdevs;
+       int j;
+
+       /* prevent more I/O from starting */
+       lock_kernel();
+
+       usbdevs = usb_get_intfdata(interface);
+       if (usbdevs==NULL){
+               CANMSG("USBCAN device seems to be already removed\n");
+               unlock_kernel();
+               return;
+       }
+       usb_set_intfdata(interface, NULL);
+       deregister_hotplug_dev(usbdevs->candev);
+
+       for (j=0;j<usbdevs->count;j++){
+               if (!usbdevs->devs[j])  continue;
+               mutex_lock(&usbdevs->devs[j]->io_mutex);
+               usbdevs->devs[j]->interface = NULL;
+               mutex_unlock(&usbdevs->devs[j]->io_mutex);
+       }
+
+       unlock_kernel();
+       
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10))
+       kref_put(&usbdevs->candev->refcount,release_device);
+#else
+       release_device(&usbdevs->candev);
+#endif
+}
+
 int usbcan_init(void){
        return usb_register(&usbcan_driver);
 }