diff --git a/fl2000_interrupt.c b/fl2000_interrupt.c index df2a14a..ccc5563 100644 --- a/fl2000_interrupt.c +++ b/fl2000_interrupt.c @@ -11,10 +11,8 @@ struct fl2000_intr { struct usb_device *usb_dev; struct drm_device *drm; - u8 poll_interval; - struct urb *urb; u8 *buf; - dma_addr_t transfer_dma; + int pipe; struct work_struct work; struct workqueue_struct *work_queue; }; @@ -23,10 +21,21 @@ static void fl2000_intr_work(struct work_struct *work) { int event; struct fl2000_intr *intr = container_of(work, struct fl2000_intr, work); - - event = fl2000_check_interrupt(intr->usb_dev); - if (event) - drm_kms_helper_hotplug_event(intr->drm); + struct usb_device *usb_dev = intr->dev; + + while (1) { + /* Receive interrupt message */ + ret = usb_interrupt_msg(usb_dev, intr->pipe, intr->buf, INTR_BUFSIZE, NULL, 0); + if (ret) { + dev_err(&usb_dev->dev, "Interrupt message failed (%d)", ret); + /* TODO: Signal fault to system and start shutdown of usb_dev */ + return; + } + + event = fl2000_check_interrupt(intr->usb_dev); + if (event) + drm_kms_helper_hotplug_event(intr->drm); + } } static void fl2000_intr_release(struct device *dev, void *res) @@ -34,40 +43,10 @@ static void fl2000_intr_release(struct device *dev, void *res) struct fl2000_intr *intr = res; struct usb_device *usb_dev = to_usb_device(dev); - usb_poison_urb(intr->urb); cancel_work_sync(&intr->work); destroy_workqueue(intr->work_queue); - usb_free_coherent(usb_dev, INTR_BUFSIZE, intr->buf, intr->transfer_dma); - usb_free_urb(intr->urb); -} - -static void fl2000_intr_completion(struct urb *urb) -{ - int ret; - struct usb_device *usb_dev = urb->dev; - struct fl2000_intr *intr = urb->context; - - ret = fl2000_urb_status(usb_dev, urb->status, urb->pipe); - if (ret) { - dev_err(&usb_dev->dev, "Stopping interrupts"); - return; - } - - /* This possibly involves reading I2C registers, etc. so better to schedule a work queue */ - queue_work(intr->work_queue, &intr->work); - - /* For interrupt URBs, as part of successful URB submission urb->interval is modified to - * reflect the actual transfer period used, so we need to restore it - */ - urb->interval = intr->poll_interval; - urb->start_frame = -1; - - /* Restart urb */ - ret = fl2000_submit_urb(urb); - if (ret) { - dev_err(&usb_dev->dev, "URB submission failed (%d)", ret); - /* TODO: Signal fault to system and start shutdown of usb_dev */ - } + + kfree(intr->buf); } /** @@ -95,6 +74,8 @@ struct fl2000_intr *fl2000_intr_create(struct usb_device *usb_dev, struct drm_de dev_err(&usb_dev->dev, "Cannot find interrupt endpoint"); return ERR_PTR(ret); } + ret = usb_endpoint_num(desc); + intr->pipe = usb_rcvintpipe(usb_dev, ret); intr = devres_alloc(&fl2000_intr_release, sizeof(*intr), GFP_KERNEL); if (!intr) { @@ -103,45 +84,24 @@ struct fl2000_intr *fl2000_intr_create(struct usb_device *usb_dev, struct drm_de } devres_add(&usb_dev->dev, intr); - intr->poll_interval = desc->bInterval; intr->usb_dev = usb_dev; intr->drm = drm; - INIT_WORK(&intr->work, &fl2000_intr_work); - - intr->urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!intr->urb) { - dev_err(&usb_dev->dev, "Allocate interrupt URB failed"); - devres_release(&usb_dev->dev, fl2000_intr_release, NULL, NULL); - return ERR_PTR(-ENOMEM); - } - - intr->buf = usb_alloc_coherent(usb_dev, INTR_BUFSIZE, GFP_KERNEL, &intr->transfer_dma); + intr->buf = kmalloc(INTR_BUFSIZE, GFP_KERNEL); if (!intr->buf) { dev_err(&usb_dev->dev, "Cannot allocate interrupt data"); devres_release(&usb_dev->dev, fl2000_intr_release, NULL, NULL); return ERR_PTR(-ENOMEM); } + /* This possibly involves reading I2C registers, etc. so better to schedule a work queue */ + INIT_WORK(&intr->work, &fl2000_intr_work); intr->work_queue = create_workqueue("fl2000_interrupt"); if (!intr->work_queue) { dev_err(&usb_dev->dev, "Create interrupt workqueue failed"); devres_release(&usb_dev->dev, fl2000_intr_release, NULL, NULL); return ERR_PTR(-ENOMEM); } - - /* Interrupt URB configuration is static, including allocated buffer */ - usb_fill_int_urb(intr->urb, usb_dev, usb_rcvintpipe(usb_dev, 3), intr->buf, INTR_BUFSIZE, - fl2000_intr_completion, intr, intr->poll_interval); - intr->urb->transfer_dma = intr->transfer_dma; - intr->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* use urb->transfer_dma */ - - /* Start checking for interrupts */ - ret = usb_submit_urb(intr->urb, GFP_KERNEL); - if (ret) { - dev_err(&usb_dev->dev, "URB submission failed"); - devres_release(&usb_dev->dev, fl2000_intr_release, NULL, NULL); - return ERR_PTR(ret); - } + queue_work(intr->work_queue, &intr->work); return intr; }