Hi, >> Here are the last two setup data and CBW data received. the >> get_next_command() is not called when CBW data is received. the >> bulk_out_complete() wakes up the thread, however, get_next_command() >> still sleeps. i do not see where req->length is checked in gadget >> driver. >> >> g_file_storage gadget: ep0-setup, length 8: >> 00000000: 00 09 01 00 00 00 00 00 >> g_file_storage gadget: set configuration >> g_file_storage gadget: ep0-setup, length 8: >> 00000000: a1 fe 00 00 00 00 01 00 >> g_file_storage gadget: get max LUN >> g_file_storage gadget: ep0-in, length 1: >> 00000000: 00 >> g_file_storage gadget: bulk-out, length 31: >> 00000000: 55 53 42 43 a8 48 ed 86 24 00 00 00 80 00 06 12 >> 00000010: 00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 >> g_file_storage gadget: bulk_out_complete --> 0, 31/0 > > file_storage uses bulk_out_intended_length. > > You're on your own, to be fair, using a really old kernel, you never > posted your UDC driver for review, so you need to fix it all up by > yourself. > > Read the code, add prints, look at other UDC drivers. g_file_storage is > next to perfect and proven to work with many, many different setups. Here is my UDC driver code. I use a kthread to poll the hardware register EP0 and EP1 interrupt. I removed the HW register access code. If it is required, i can send the full code. Something could be wrong in my UDC driver. Thanks. #include <linux/device.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/kthread.h> #include <linux/delay.h> #define NUM_ENDPOINTS 3 #define EP_MAX_PACKET_SIZE 0x200 #define EP0_MAX_PACKET_SIZE 64 #define QH_MAXNUM 32 /*-------------------------------------------------------------*/ #define IO_OFFSET 0x55000000 #define __IO_ADDRESS(x) ((x) + IO_OFFSET) #define IO_ADDRESS(pa) IOMEM(__IO_ADDRESS(pa)) #ifdef IOMEM // Override asm/io.h #undef IOMEM #endif // IOMEM #ifdef __ASSEMBLER__ #define IOMEM(x) x #else #define IOMEM(x) ((void __force __iomem *)(x)) #endif #define ka2000_readb(a) __raw_readb(IO_ADDRESS(a)) #define ka2000_readw(a) __raw_readw(IO_ADDRESS(a)) #define ka2000_readl(a) __raw_readl(IO_ADDRESS(a)) #define ka2000_writeb(v, a) __raw_writeb(v, IO_ADDRESS(a)) #define ka2000_writew(v, a) __raw_writew(v, IO_ADDRESS(a)) #define ka2000_writel(v, a) __raw_writel(v, IO_ADDRESS(a)) /*-------------------------------------------------------------*/ static const char *reqname(unsigned r) { switch (r) { case USB_REQ_GET_STATUS: return "GET_STATUS"; case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE"; case USB_REQ_SET_FEATURE: return "SET_FEATURE"; case USB_REQ_SET_ADDRESS: return "SET_ADDRESS"; case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR"; case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR"; case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION"; case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION"; case USB_REQ_GET_INTERFACE: return "GET_INTERFACE"; case USB_REQ_SET_INTERFACE: return "SET_INTERFACE"; default: return "*UNKNOWN*"; } } static struct usb_endpoint_descriptor ep0_out_desc = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0, .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; static struct usb_endpoint_descriptor ep0_in_desc = { .bLength = sizeof(struct usb_endpoint_descriptor), .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_CONTROL, }; struct kagen2; struct kagen2_request { struct usb_request req; struct list_head queue; unsigned mapped:1, valid:1; }; struct ka_ep { struct usb_ep ep; struct kagen2 * dev; unsigned long irqs; struct usb_request req; struct list_head queue; const struct usb_endpoint_descriptor *desc; unsigned num:8, fifo_size:12, stopped:1, wedged:1, is_in:1, is_iso:1, dma:1, not_empty:1; }; struct ka_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; }; #define CONFIG_MAX_PKT(n) ((n) << 16) #define TERMINATE 1 #define INFO_BYTES(n) ((n) << 16) #define INFO_IOC (1 << 15) #define INFO_ACTIVE (1 << 7) #define INFO_HALTED (1 << 6) #define INFO_BUFFER_ERROR (1 << 5) #define INFO_TX_ERROR (1 << 3) static struct ka_ep ka_ep_g[NUM_ENDPOINTS]; enum SPEED { LOWSPEED = 0, FULLSPEED = 1, HIGHSPEED = 2, }; enum STATE { DEFAULT = 0, SUSPENDED }; int system_level = 0; unsigned char device_state = 0; unsigned char device_speed = FULLSPEED; static void handle_ep_complete(struct ka_ep *ka_ep_p, struct kagen2_request *req) { int num, in; printk("%s %x\n",__func__, ka_ep_p->desc); num = ka_ep_p->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ka_ep_p->desc->bEndpointAddress & USB_DIR_IN) != 0; if (num == 0) ka_ep_p->desc = &ep0_out_desc; //ep->req.length -= len; printk("ept%d %s complete %x\n", num, in ? "in" : "out", ka_ep_p->req.length); // call gadget code ka_ep_p->req.complete(&ka_ep_p->ep, &req->req); if (num == 0) { ka_ep_p->req.length = 0; usb_ep_queue(&ka_ep_p->ep, &req->req, 0); ka_ep_p->desc = &ep0_in_desc; } } /*--------------------------------------------------------------------------*/ static int kagen2_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc); static int kagen2_ep_disable(struct usb_ep *ep); static int kagen2_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags); static int kagen2_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req); static struct usb_request * kagen2_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags); static void kagen2_ep_free_request(struct usb_ep *ep, struct usb_request *_req); static int kagen2_get_frame(struct usb_gadget *_gadget); static int kagen2_wakeup(struct usb_gadget *_gadget); static int kagen2_set_selfpowered(struct usb_gadget *_gadget, int value); static int kagen2_pullup(struct usb_gadget *_gadget, int is_on); static int kagen2_start(struct usb_gadget *_gadget, struct usb_gadget_driver *driver); static int kagen2_stop(struct usb_gadget *_gadget, struct usb_gadget_driver *driver); #define DRIVER_DESC "KAgen2 USB Peripheral Controller" static const char driver_name[] = "kagen2_usb"; static const char driver_vers[] = "2012 december"; static const char driver_desc[] = DRIVER_DESC; static const char ep0name[] = "ep0"; static const char * const ep_name[] = { ep0name, "ep1", "ep1", }; struct kagen2 { /* each device provides one gadget, several endpoints */ struct usb_gadget gadget; struct device *dev; unsigned short dev_id; spinlock_t lock; struct ka_ep ep[NUM_ENDPOINTS]; struct usb_gadget_driver *driver; unsigned protocol_stall:1, softconnect:1, is_selfpowered:1, wakeup:1, dma_eot_polarity:1, dma_dack_polarity:1, dma_dreq_polarity:1, dma_busy:1; u16 chiprev; u8 pagesel; unsigned int irq; unsigned short fifo_mode; void __iomem *base_addr; }; #define SETUP(type, request) (((type) << 8) | (request)) static void handle_setup(struct kagen2 * dev, int bmRequestType, int bRequest, int wValue, int wIndex, int wLength) { struct usb_request *req = &(dev->ep[0].req); struct usb_ctrlrequest r; int status = 0; int num, in, _num, _in, i; char *buf; if (wLength == 0 && wValue == 0) return; r.bRequestType = bmRequestType; r.bRequest = bRequest; r.wValue = wValue; r.wIndex = wIndex; r.wLength = wLength; printk("handle setup %s, %x, %x index %x value %x len %x\n", reqname(r.bRequest), r.bRequestType, r.bRequest, r.wIndex, r.wValue, r.wLength); switch (SETUP(r.bRequestType, r.bRequest)) { case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): printk("USB_RECIP_ENDPOINT\n"); _num = r.wIndex & 15; _in = !!(r.wIndex & 0x80); if ((r.wValue == 0) && (r.wLength == 0)) { req->length = 0; for (i = 0; i < NUM_ENDPOINTS; i++) { if (!dev->ep[i].desc) continue; num = dev->ep[i].desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (dev->ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; if ((num == _num) && (in == _in)) { //ep_enable(num, in); usb_ep_queue(dev->gadget.ep0, req, 0); break; } } } return; case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS): /* * write address delayed (will take effect * after the next IN txn) */ printk("USB_RECIP_DEVICE\n"); req->length = 0; usb_ep_queue(dev->gadget.ep0, req, 0); return; case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS): printk("USB_REQ_GET_STATUS\n"); req->length = 2; buf = (char *)req->buf; buf[0] = 1 << USB_DEVICE_SELF_POWERED; buf[1] = 0; usb_ep_queue(dev->gadget.ep0, req, 0); return; } /* pass request up to the gadget driver */ if (!dev->driver) { printk("driver not defined\n"); return; } if (!dev->driver->setup) { printk("setup not defined\n"); return; } status = dev->driver->setup(&dev->gadget, &r); printk("status %d\n", status); return; } static void ep1_out(struct kagen2 * dev) { unsigned int val_arr[16]; int len; int i, in, num; unsigned int val; //EP1 OUT IRQ // get byte cnt val = readl(dev->base_addr + 0x008); len = val & 0xFF; // read from fifo1 data for (i = 0; i < len/4; i++) { val_arr[i] = readl(dev->base_addr + 0x084); printk("0x%x ",val_arr[i]); } if ((len%4) != 0) { val_arr[i] = readl(dev->base_addr + 0x084); } //bulk out ep if (dev->ep[2].desc) { struct kagen2_request * ka_req; struct usb_request * req; if (list_empty(&dev->ep[2].queue)) { printk( "%s: RX DMA done : NULL REQ on OUT EP-1\n", __func__); //return; } //req = list_entry(dev->ep[2].queue.next, struct kagen2_request, queue); req = &dev->ep[2].req; ka_req = container_of(req, struct kagen2_request, req); if (ka_req == NULL) { printk("req NULL\n"); return; } //ka_req->req.length = len; ka_req->req.actual = len; memcpy(ka_req->req.buf, &val_arr[0], len); num = dev->ep[2].desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (dev->ep[2].desc->bEndpointAddress & USB_DIR_IN) != 0; printk("epnum %d in %d\n", num, in); printk("len %d\n", len); handle_ep_complete(&dev->ep[2], ka_req); } } static irqreturn_t kagen2_irq(int irq, void * _dev) { unsigned int val; int i, in, num; struct kagen2 *dev = _dev; return IRQ_HANDLED; } static void kagen2_lowlevel_init(struct kagen2 * dev) { // initialize the hardware } static struct usb_ep_ops kagen2_ep_ops = { .enable = kagen2_ep_enable, .disable = kagen2_ep_disable, .alloc_request = kagen2_ep_alloc_request, .free_request = kagen2_ep_free_request, .queue = kagen2_ep_queue, .dequeue = kagen2_ep_dequeue, }; static struct usb_gadget_ops kagen2_gadget_ops = { .get_frame = kagen2_get_frame, .wakeup = kagen2_wakeup, .set_selfpowered = kagen2_set_selfpowered, .pullup = kagen2_pullup, .udc_start = kagen2_start, .udc_stop = kagen2_stop, }; static int kagen2_get_frame(struct usb_gadget *_gadget) { struct kagen2 *dev; unsigned long flags; u16 ret; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct kagen2, gadget); spin_lock_irqsave(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags); return ret; } static int kagen2_wakeup(struct usb_gadget *_gadget) { struct kagen2 *dev; unsigned long flags; if (!_gadget) return 0; dev = container_of(_gadget, struct kagen2, gadget); spin_lock_irqsave(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags); return 0; } static int kagen2_set_selfpowered(struct usb_gadget *_gadget, int value) { struct kagen2 *dev; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct kagen2, gadget); dev->is_selfpowered = value; return 0; } static int kagen2_pullup(struct usb_gadget *_gadget, int is_on) { struct kagen2 *dev; unsigned long flags; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct kagen2, gadget); spin_lock_irqsave(&dev->lock, flags); dev->softconnect = (is_on != 0); spin_unlock_irqrestore(&dev->lock, flags); return 0; } static int kagen2_start(struct usb_gadget *_gadget, struct usb_gadget_driver *driver) { struct kagen2 *dev; unsigned i; struct ka_ep *ka_ep; printk("0x%x 0x%x\n", driver, driver->setup); //if (!driver || !driver->unbind || !driver->setup || // driver->max_speed != USB_SPEED_HIGH) if (!driver) return -EINVAL; dev = container_of(_gadget, struct kagen2, gadget); printk("%s\n", __func__); printk("0x%x 0x%x\n", driver, driver->setup); for (i = 0; i < 4; ++i) dev->ep[i].irqs = 0; /* hook up the driver ... */ dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; dev->gadget.speed = USB_SPEED_HIGH; //kagen2_ep0_start(); ka_ep = &dev->ep[0]; printk("0x%x 0x%x\n", &ka_ep->ep, &ep0_in_desc); kagen2_ep_enable(&ka_ep->ep, &ep0_in_desc); //ka_ep = &dev->ep[1]; //kagen2_ep_enable(&ka_ep->ep, &ep0_out_desc); return 0; } static int kagen2_stop(struct usb_gadget *_gadget, struct usb_gadget_driver *driver) { struct kagen2 *dev; unsigned long flags; dev = container_of(_gadget, struct kagen2, gadget); spin_lock_irqsave(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags); dev->gadget.dev.driver = NULL; dev->driver = NULL; return 0; } static int kagen2_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { struct ka_ep *ka_ep = container_of(ep, struct ka_ep, ep); int num, in; num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (desc->bEndpointAddress & USB_DIR_IN) != 0; ka_ep->desc = desc; printk("%s %d %d\n",__func__, num, in); printk("%x %x %x \n", ep, desc, ka_ep); return 0; } static int kagen2_ep_disable(struct usb_ep *ep) { return 0; } static struct usb_request * kagen2_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { struct ka_ep *ka_ep = container_of(ep, struct ka_ep, ep); return &ka_ep->req; } static void kagen2_ep_free_request(struct usb_ep *ep, struct usb_request *_req) { return; } static void ep0_in(unsigned int phys, int len, struct kagen2 * dev) { unsigned int val32; unsigned int iter_num = 0; int i; // send to EP0 IN buffer return; } static void ep1_in(unsigned int phys, int len, struct kagen2 * dev) { unsigned int iter_num = 0, len_num = 0; unsigned int val32; int i; int num = 1; // send to EP1 IN buffer return; } static int kagen2_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { struct ka_ep *ka_ep; struct kagen2_request *ka_req; struct kagen2 * dev; unsigned phys; int num, len, in; printk("%s\n",__func__); ka_req = container_of(req, struct kagen2_request, req); if (!req || !req->complete || !req->buf || !list_empty(&ka_req->queue)) return -EINVAL; ka_ep = container_of(ep, struct ka_ep, ep); if (!ep || (!ka_ep->desc && ka_ep->num != 0)) return -EINVAL; printk("%s %x %x\n",__func__, ka_ep, ka_ep->desc); dev = ka_ep->dev; //printk("0x%x 0x%x 0x%x 0x%x\n", dev, dev->driver, ka_ep, ka_req); if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; num = ka_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ka_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; phys = (unsigned)req->buf; len = req->length; printk("ept%d %s queue len 0x%x, buffer 0x%x\n", num, in ? "in" : "out", len, phys); if ((len > 0) && (num == 0) && (in != 0)) { req->actual = 0; ep0_in(phys, len, dev); req->actual += len; req->complete(ep, req); return 0; } else if ((len > 0) && (num == 1) && (in != 0)) { ep1_in(phys, len, dev); return len; } else if (in == 0) { // read from EPxOUT buffer if (num == 1) { //struct usb_request * temp = &(ka_ep->req); printk("EP1 %x\n", (u8 *)ka_ep) ; list_add_tail(&ka_req->queue, &ka_ep->queue); } } return 0; } static int kagen2_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct ka_ep *ep; struct kagen2_request *req; unsigned long flags; ep = container_of(_ep, struct ka_ep, ep); spin_lock_irqsave(&ep->dev->lock, flags); /* make sure it's still queued on this endpoint */ list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) break; } if (&req->req != _req) { spin_unlock_irqrestore(&ep->dev->lock, flags); return -EINVAL; } req = NULL; spin_unlock_irqrestore(&ep->dev->lock, flags); return 0; } static void kagen2_gadget_release(struct device *_dev) { struct kagen2 *dev = dev_get_drvdata(_dev); kfree(dev); } // for kernel thread struct task_struct * task; static int example_thread(void * data) { int i = 0; volatile unsigned int val; struct kagen2 * dev = (struct kagen2 *)data; for (;;) { { i = 0; val = readl(dev->base_addr + 0x1a0); val = val & 0xff; //printk("INTC IVECT %x\n", val); if (val == 0xd8) { //peripirq val = readl(dev->base_addr + 0x1bc); val &= 0xffffff00; val |= 0x00000011; writel(val, dev->base_addr + 0x1bc); } else if (val == 0x00) { int bmRequestType; int bRequest; unsigned int wLength, wValue, wIndex; unsigned int rdata, rdata1; // setup data valid val = readl(dev->base_addr + 0x18c); val &= 0xffffff00; val |= 0x00000001; writel(val, dev->base_addr + 0x18c); // process the setup data rdata = readl(dev->base_addr + 0x180) ; rdata1 = readl(dev->base_addr + 0x184); bmRequestType = rdata & 0xff; bRequest = (rdata >> 8) & 0xff; wValue = (rdata >> 16) & 0xffff; wIndex = rdata1 & 0xffff; wLength = (rdata1 >> 16) & 0xffff; handle_setup(dev, bmRequestType, bRequest, wValue, wIndex, wLength); } else if (val == 0x28) { printk("EPx OUT IRQ 0x%x\n", val); ep1_out(dev); } } } return 0; } // end for kernel thread static int __devinit kagen2_plat_probe(struct platform_device *pdev) { struct kagen2 *dev; int ret; int i; unsigned int irqflags; resource_size_t base, len; struct resource *iomem, *irq_res; printk("kagen2_plat_probe 1\n"); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!irq_res || !iomem) { printk("kagen2_plat_probe 2\n"); return -EINVAL; } if (!irq_res->start) { printk("kagen2_plat_probe 3\n"); return (-ENODEV); } /* alloc, and start init */ dev = kzalloc(sizeof(struct kagen2), GFP_KERNEL); if (!dev) { printk("kagen2_plat_probe 4\n"); return (-ENOMEM); } printk("kagen2_plat_probe 5\n"); spin_lock_init(&dev->lock); dev->irq = irq_res->start; dev->dev = &(pdev->dev); dev->gadget.ops = &kagen2_gadget_ops; dev->gadget.max_speed = USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ dev_set_name(&dev->gadget.dev, "gadget"); dev->gadget.dev.parent = &(pdev->dev); dev->gadget.dev.release = kagen2_gadget_release; dev->gadget.name = driver_name; irqflags = 0; if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) irqflags |= IRQF_TRIGGER_RISING; if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) irqflags |= IRQF_TRIGGER_FALLING; if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) irqflags |= IRQF_TRIGGER_HIGH; if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) irqflags |= IRQF_TRIGGER_LOW; base = iomem->start; len = resource_size(iomem); if (!request_mem_region(base, len, driver_name)) { ret = -EBUSY; goto err; } dev->base_addr = ioremap_nocache(base, len); if (!dev->base_addr) { ret = -EFAULT; goto err_req; } printk("kagen2_plat_probe 6, 0x%x 0x%x\n", dev->base_addr, len); // init low level usb kagen2_lowlevel_init(dev); // init usb software structure for (i = 0; i < NUM_ENDPOINTS; ++i) { struct ka_ep *ep = &dev->ep[i]; printk("kagen2_plat_probe %d %x\n", i, &dev->ep[i]); ep->ep.name = ep_name[i]; ep->dev = dev; ep->desc = NULL; INIT_LIST_HEAD(&ep->queue); ep->ep.maxpacket = ~0; ep->ep.ops = &kagen2_ep_ops; } dev->ep[0].ep.maxpacket = 64; dev->gadget.ep0 = &dev->ep[0].ep; INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); printk("kagen2_plat_probe 8\n"); INIT_LIST_HEAD(&dev->gadget.ep_list); list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512; dev->ep[1].ep.maxpacket = dev->ep[2].ep.maxpacket = 512; // unmask the irq 32 which is the usb irq //ka2000_writel((ka2000_readl(0xa0006014) & 0xfffffffe), 0xa0006014); ret = device_register(&dev->gadget.dev); ret = usb_add_gadget_udc(dev->dev, &dev->gadget); platform_set_drvdata(pdev, dev); // kernel thread task = kthread_run(&example_thread, (void *)dev, "example_t"); return 0; err_req: err: return ret; } static int __devexit kagen2_plat_remove(struct platform_device *pdev) { struct kagen2 *dev = platform_get_drvdata(pdev); release_mem_region(pdev->resource[0].start, resource_size(&pdev->resource[0])); kfree(dev); return 0; } /*-------------------------------------------------------------------------*/ static struct platform_driver kagen2_plat_driver = { .probe = kagen2_plat_probe, .remove = kagen2_plat_remove, .driver = { .name = driver_name, .owner = THIS_MODULE, }, }; static int __init kagen2_init (void) { int ret; printk("kagen2_init\n"); ret = platform_driver_register(&kagen2_plat_driver); printk("kagen2_init %d\n", ret); return ret; } module_init (kagen2_init); static void __exit kagen2_cleanup (void) { platform_driver_unregister(&kagen2_plat_driver); kthread_stop(task); } module_exit (kagen2_cleanup); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("KA"); MODULE_LICENSE("GPL"); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html