From fdf877c54ec2598864f6193e8e1fbaedb5c05efb Mon Sep 17 00:00:00 2001 From: "Ing. Jan Kriz" Date: Sun, 17 Apr 2011 19:39:36 +0200 Subject: [PATCH] reference count for device opens added (helps while asynchronous remove of the USB device occurs) --- lincan/include/can_sysdep.h | 3 + lincan/include/main.h | 10 +++ lincan/include/usbcan.h | 1 + lincan/src/close.c | 10 +++ lincan/src/hcan2.c | 4 +- lincan/src/main.c | 22 +++++- lincan/src/open.c | 4 + lincan/src/setup.c | 4 + lincan/src/usbcan.c | 154 +++++++++++++++++++----------------- 9 files changed, 134 insertions(+), 78 deletions(-) diff --git a/lincan/include/can_sysdep.h b/lincan/include/can_sysdep.h index ff06946..5ade341 100644 --- a/lincan/include/can_sysdep.h +++ b/lincan/include/can_sysdep.h @@ -56,6 +56,9 @@ #include #include #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)) + #include +#endif #include #include diff --git a/lincan/include/main.h b/lincan/include/main.h index 01f8739..c82935b 100644 --- a/lincan/include/main.h +++ b/lincan/include/main.h @@ -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); diff --git a/lincan/include/usbcan.h b/lincan/include/usbcan.h index f29f9b8..35136da 100644 --- a/lincan/include/usbcan.h +++ b/lincan/include/usbcan.h @@ -188,6 +188,7 @@ struct usbcan_devs { struct usbcan_usb **devs; int count; struct candevice_t *candev; + struct usb_device *udev; }; diff --git a/lincan/src/close.c b/lincan/src/close.c index a69247c..157acfa 100644 --- a/lincan/src/close.c +++ b/lincan/src/close.c @@ -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; } diff --git a/lincan/src/hcan2.c b/lincan/src/hcan2.c index 23d15ab..f81ae58 100644 --- a/lincan/src/hcan2.c +++ b/lincan/src/hcan2.c @@ -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 */ diff --git a/lincan/src/main.c b/lincan/src/main.c index 8018b58..367c98d 100644 --- a/lincan/src/main.c +++ b/lincan/src/main.c @@ -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;ihostdevice != dev) continue; diff --git a/lincan/src/open.c b/lincan/src/open.c index 535c2c3..f0c3fd2 100644 --- a/lincan/src/open.c +++ b/lincan/src/open.c @@ -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; diff --git a/lincan/src/setup.c b/lincan/src/setup.c index b051d04..e36d8ef 100644 --- a/lincan/src/setup.c +++ b/lincan/src/setup.c @@ -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; diff --git a/lincan/src/usbcan.c b/lincan/src/usbcan.c index a5e938a..19b1dc6 100644 --- a/lincan/src/usbcan.c +++ b/lincan/src/usbcan.c @@ -11,13 +11,18 @@ #include "../include/devcommon.h" #include "../include/setup.h" #include "../include/usbcan.h" - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) #include #endif +#include 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;jcount;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;jcount;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;jcount;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); } -- 2.39.2