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, &param->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, &param->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, &param->sString, ADU_MSG_LL, 
                                                        &iRead, 1000, 10);
                } // end if
        } // end if
        
        if (code == ADUHID_STRING)
        {
                retval = usb_string(udev, param->index, (char*)&param->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

Reply via email to