My apologies to the group about the content of this thread. My original message was E-mailed offline but the replies from others appeared on linux-usb-devel.
I was attempting to explain my motives for moving my experimental driver development off-line from the group. Any deficiencies are in MY attitudes, interests and abilities. I never received a rude or antagonistic reply to any of my posts, not even once. Everyone who replied was helpful and constructive. With the information gleaned from the group it is now time for ME to get MY nose to the grind-stone and build something. (With help from a few others that I met through the group.) When the code has been built and tested THEN some clean code can be submit to the group for peer review. End of apology. Back to regular programming: Jeff, thanks for the web link for the Lego USB Tower driver. That looks remarkably similar to the experimental 2.5 code that I have been working on. Hooray, that means that a 2.4 driver built in this fashion will be easy to move to 2.6 >From a quick read of the code, it appears that I can easily modify it to drive an ADU device. Similar changes should make it drive an Evolution Robots device. Aside: Like Jeff, I had hoped to use stock Linux API calls to control my device. This would eliminate the need for me to baby-sit the code over the years as Linux evolves. A custom driver must be re-tested every time a major revision to the kernel occurs. If the driver uses common functions then it is less likely to break. The HID device interface would appear to be the answer except the hiddev.c source code for 2.4.20 and 2.5.59 reads: static ssize_t hiddev_write(...... { return -EINVAL; } If this actually wrote the buffer to the device then I would have used the hiddev exclusively...... (I know there are problems with implementing hiddev_write, but Microsoft has overcome similar problems with Windows). WARNING: BRAIN ENGAGED.... thoughts are processing...... Hey, maybe I could implement hiddev_write myself and submit the code for inclusion in hiddev.c...... Naw, any implementation would have to make numerous assumptions about the characteristics of the USB device to the point of not being a generic solution. Non-generic solutions are not acceptable for inclusion in core kernel processing. End of Aside Lack Of Vendor Interest ------------------------------------ The engineers at Ontrak Control Systems have been "helpful but disinterested". They do not perceive Linux as a viable market for their devices. Whenever I mention Linux they respond that they would like a Macintosh driver instead. Go figure. So anything you see here is done on my own volition. Where To Now. ------------------------ 1) The ocsadu.c file embedded below is for kernel 2.5.54. Do not use it. It requires no changes to the kernel other than suppressing the HID claiming of the ADU device. I simply use /sbin/insmod to activate the module. (of course I have Rusty's module patches applied.) I have included the source so that you can see how similar it is to the Lego USB driver. (Just reassurance that anything done for 2.4 will be easy to port to 2.5 and 2.6) 2) My next step is to copy the Lego USB Tower driver and change it to handle an ADU device. I will follow their coding standards. When I am finished you can run a diff to find out what changes were required. This might help you with the Evolution Robots driver. 3) Thanks for your interest in my USB message monitoring technique. I will compose a message about how I used the ADU hardware and a PIC burner to learn about USB (ie, my PIC message tracker). Look for it on this group..... soon. Blue Sky (hopes and dreams) ----------------------------------------------- Maybe.... (JUST maybe) I will try to implement the hiddev_write function as an experiment for my own interest. If time permits. If any REAL kernel developers have read this far maybe they might see this as an interesting challenge. Thanks for your attention. John Homppi The remainder of this message is an ASCII in-line copy of ocsadu.c // ocsadu.c - Experimental driver for Ontrak Control Systems ADU devices // built for Kernel 2.5.54 // Based upon the usbtest.c sample program. // Copyright 2003 by John Homppi // Released under the conditions of the Gnu Public Licence. // Use of this program from your application code does not cause your // program to become a derived work. // This program has no warranty express or implied. // You are responsible for all testing to ensure that it works // for your purposes. // Assume that this program is broken unless you can prove // otherwise to yourself. // Use at your own risk. // There is absolutely no support provided for this program. // Even if it appears to work, it may fail at any time. #define __KERNEL__ // activate kernel include options #define MODULE // activate module include option #include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/sem.h> #include <linux/module.h> #include <linux/pci.h> /* for scatterlist macros */ #include <linux/usb.h> #include <asm/byteorder.h> #include "hcd.h" /* for usbcore internals */ #if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) # define DEBUG #endif #define ADU_MSG_LL 8 #define ONTRAK 0x0a07 // Ontrak Control Systems Inc. #define ADU100 0x0064 // product id is numeric portion of model# #define ADU200 0x00c8 static int vendor = ONTRAK; static int product0 = ADU100; static int product1 = ADU200; // function declarations static int ocsadu_probe (struct usb_interface *intf, const struct usb_device_id *id); static int ocsadu_ioctl (struct usb_interface *intf, unsigned int code, void *buf); static void ocsadu_disconnect (struct usb_interface *intf); // our data structures struct ocsadu_info { const char *name; u8 ep_in; // interrupt endpoint in u8 ep_out; // interrupt endpoint out int alt; }; struct usb_api_data { wait_queue_head_t wqh; int done; }; // this driver is accessed via usbfs ioctl calls. struct ocsadu_dev { struct usb_interface *intf; struct ocsadu_info *info; char id [32]; int in_pipe; int out_pipe; struct semaphore sem; #define TBUF_SIZE 256 u8 *buf; }; struct ocsadu_param { int index; // buffer char sString[TBUF_SIZE]; }; #define ADUHID_WRITE _IOWR('U', 100, struct ocsadu_param) #define ADUHID_READ _IOWR('U', 101, struct ocsadu_param) #define ADUHID_STRING _IOWR('U', 102, struct ocsadu_param) // Ontrak ADU device static struct ocsadu_info adu_info = { .name = "Ontrak ADU device", .ep_in = 1, .ep_out = 1, .alt = -1, }; // table of device id's static struct usb_device_id id_table [] = { { USB_DEVICE (ONTRAK, ADU100), .driver_info = (unsigned long) &adu_info, }, { USB_DEVICE (ONTRAK, ADU200), .driver_info = (unsigned long) &adu_info, }, { } }; MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver ocsadu_driver = { .owner = THIS_MODULE, .name = "ocsadu", .id_table = id_table, .probe = ocsadu_probe, .ioctl = ocsadu_ioctl, .disconnect = ocsadu_disconnect, }; static struct usb_device *testdev_to_usbdev (struct ocsadu_dev *test) { return interface_to_usbdev (test->intf); } // end testdev_to_usbdev // -------------------------------------------------------------------------- // functions mimicking message.c static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs) { struct usb_api_data *awd = (struct usb_api_data *)urb->context; warn("in usb_api_blocking_completion - status=%d", urb->status); awd->done = 1; wmb(); wake_up(&awd->wqh); } // end usb_api_blocking_completion // Starts urb and waits for completion or timeout static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) { DECLARE_WAITQUEUE(wait, current); struct usb_api_data awd; int status; init_waitqueue_head(&awd.wqh); awd.done = 0; set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&awd.wqh, &wait); urb->context = &awd; status = usb_submit_urb(urb, GFP_ATOMIC); //status = usb_submit_urb(urb, SLAB_KERNEL); if (status) { warn("submission of urb failed. status=%d", status); // something went wrong usb_free_urb(urb); set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); return status; } // end if warn("starting timeout loop"); while (timeout && !awd.done) { warn("timeout but not done. status=%d", urb->status); timeout = schedule_timeout(timeout); warn("after schedule timeout, timeout=%d", timeout); set_current_state(TASK_UNINTERRUPTIBLE); rmb(); } // end while warn("after timeout loop"); set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); if (!timeout && !awd.done) { warn("no timeout and not done"); if (urb->status != -EINPROGRESS) { // No callback?!! printk(KERN_ERR "usb: raced timeout, " "pipe 0x%x status %d time left %d\n", urb->pipe, urb->status, timeout); status = urb->status; } else { warn("usb_control/bulk_msg: timeout"); usb_unlink_urb(urb); // remove urb safely status = -ETIMEDOUT; } // end if } else { status = urb->status; } // end if warn("status is %d", status); if (actual_length) { *actual_length = urb->actual_length; } // end if usb_free_urb(urb); return status; } // end usb_start_wait_urb //------------------------------------------------------------------- // // usb_int_msg - Builds an interrupt urb, sends it off and waits for completion // @usb_dev: pointer to the usb device to send the message to // @pipe: endpoint "pipe" to send the message to // @data: pointer to the data to send // @len: length in bytes of the data to send // @actual_length: pointer to a location to put the actual length transferred in bytes // @timeout: time in jiffies to wait for the message to complete before // timing out (if 0 the wait is forever) // @interval: usb interval // Context: !in_interrupt () // // This function sends a simple interrupt message to a specified endpoint // and waits for the message to complete, or timeout. // // If successful, it returns 0, otherwise a negative error number. // The number of actual bytes transferred will be stored in the // actual_length paramater. // // Don't use this function from within an interrupt context, like a // bottom half handler. If you need an asynchronous message, or need to // send a message from within interrupt context, use usb_submit_urb() // If a thread in your driver uses this call, make sure your disconnect() // method can wait for it to complete. Since you don't have a handle on // the URB used, you can't cancel the request. // static int usb_int_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout, int interval) { struct urb *urb; int iRC; if (len < 0) return -EINVAL; urb=usb_alloc_urb(0, GFP_KERNEL); if (!urb) return -ENOMEM; usb_fill_int_urb(urb, usb_dev, pipe, 0, len, usb_api_blocking_completion, 0, interval); //urb->transfer_buffer = usb_buffer_alloc(usb_dev,len, SLAB_KERNEL, // &urb->transfer_dma); urb->transfer_buffer = kmalloc(256, GFP_ATOMIC); if (usb_pipeout(pipe)) { //copy_from_user(urb->transfer_buffer, data, len); //warn("after copy_from_user buffer is %s", urb->transfer_buffer); strncpy(urb->transfer_buffer, data, len); warn("after strncpy buffer is %s", urb->transfer_buffer); } urb->transfer_flags |= URB_NO_INTERRUPT; iRC = usb_start_wait_urb(urb,timeout,actual_length); if (usb_pipein (pipe)) { strncpy(data, urb->transfer_buffer, len); } kfree(urb->transfer_buffer); return iRC; } // end usb_int_msg //------------------------------------------------------------------------- // ocsadu functions static int ocsadu_init (void) { dbg ("vendor=0x%04x products=0x%04x, 0x%04x", vendor, product0, product1); return usb_register (&ocsadu_driver); } // end ocsadu_init module_init (ocsadu_init); static void ocsadu_exit (void) { dbg("ocsadu_exit"); usb_deregister (&ocsadu_driver); } // end ocsadu_exit module_exit (ocsadu_exit); static void ocsadu_disconnect (struct usb_interface *intf) { struct ocsadu_dev *dev = usb_get_intfdata (intf); dbg("ocsadu_disconnect"); down (&dev->sem); usb_set_intfdata (intf, NULL); info ("unbound %s", dev->id); } // end ocsadu_disconnect //------------------------------------------------------------------------- // handle i/o requests static int ocsadu_ioctl (struct usb_interface *intf, unsigned int code, void *buf) { struct ocsadu_dev *dev = usb_get_intfdata(intf); struct usb_device *udev = testdev_to_usbdev(dev); struct ocsadu_param *param = buf; int retval = -EOPNOTSUPP; dbg("ocsadu_ioctl %s code=%d buf=%s", __TIME__, code, param->sString); if (down_interruptible(&dev->sem)) { return -ERESTARTSYS; } // end if if (code == ADUHID_WRITE) { int iWritten; retval = usb_int_msg(udev, dev->out_pipe, ¶m->sString, ADU_MSG_LL, &iWritten, 5000, 10); // interval=0 gives "invalid argument" even though it supposedly means // one-shot (ditto for inbound interrupt end-points) dbg("after write retval=%d, iWritten=%d", retval, iWritten); } // end if if (code == ADUHID_READ) { int iRead; retval = usb_int_msg(udev, dev->in_pipe, ¶m->sString, ADU_MSG_LL, &iRead, 1000, 10); dbg("after read retval=%d, iRead=%d", retval, iRead); if (retval == -ETIMEDOUT) { dbg("clearing the pipe"); // clear the pipe usb_int_msg(udev, dev->in_pipe, ¶m->sString, ADU_MSG_LL, &iRead, 1000, 10); } // end if } // end if if (code == ADUHID_STRING) { retval = usb_string(udev, param->index, (char*)¶m->sString, 256); dbg("usbstring %d: retval=%d string=%s", param->index, retval, param->sString); } // end if up(&dev->sem); return retval; } // end ocsadu_ioctl //------------------------------------------------------------------------- static int ocsadu_probe (struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; struct ocsadu_dev *dev; struct ocsadu_info *info; udev = interface_to_usbdev(intf); // is it one of ours? if (udev->descriptor.idVendor != (u16)vendor) { return -ENODEV; } // end if if (udev->descriptor.idProduct != (u16)product0 && udev->descriptor.idProduct != (u16)product1) { return -ENODEV; } // end if dbg ("matched module params, vend=0x%04x prod=0x%04x", udev->descriptor.idVendor, udev->descriptor.idProduct); dev = kmalloc (sizeof *dev, SLAB_KERNEL); if (!dev) { return -ENOMEM; } // end if memset (dev, 0, sizeof *dev); info = (struct ocsadu_info *) id->driver_info; dev->info = info; init_MUTEX (&dev->sem); // use the same kind of id the hid driver shows snprintf (dev->id, sizeof dev->id, "%s-%s:%d", udev->bus->bus_name, udev->devpath, intf->altsetting [0].desc.bInterfaceNumber); dev->intf = intf; // cacheline-aligned scratch for i/o if ((dev->buf = kmalloc (TBUF_SIZE, SLAB_KERNEL)) == 0) { kfree (dev); return -ENOMEM; } // end if if (info->ep_in) { dev->in_pipe = usb_rcvintpipe (udev, info->ep_in); } // end if if (info->ep_out) { dev->out_pipe = usb_sndintpipe (udev, info->ep_out); } // end if usb_set_intfdata (intf, dev); info ("ocsadu probed"); return 0; } // end ocsadu_probe MODULE_DESCRIPTION ("Ontrak ADU Driver"); MODULE_LICENSE ("GPL"); static char sCopyright[] = "Copyright 2003 by John Homppi "; static char sDisclaimer[] = "No warranty express or implied. Use at own risk. "; static char sAssumption[] = "Assume that this program is broken. " "You are responsible for all testing to ensure fitness for your purposes."; ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel