Hello,

here's the first version of the USB CDC OBEX driver. I have tested it with 
my Nokia 7610 phone and it seems to work fine. Feedback is welcome, 
especially concering the code quality, because I didn't do kernel 
programming before :) All the documentation/explanations/examples are in 
the patch itself.

Greg, can you please assign a minor number to this driver?

-- 
Alexander

Homepage: http://www.sensi.org/~ak/

Signed-off-by: Alex Kanavin <[EMAIL PROTECTED]>

diff -x sysfs.c -uNr linux-2.6.8-1.521/CREDITS linux-2.6.8-1.521-ak/CREDITS
--- linux-2.6.8-1.521/CREDITS   2004-08-14 08:37:26.000000000 +0300
+++ linux-2.6.8-1.521-ak/CREDITS        2004-09-25 15:19:06.000000000 +0300
@@ -1577,6 +1577,14 @@
 S: 8103 Rein
 S: Austria
 
+N: Alexander Kanavin
+E: [EMAIL PROTECTED]
+W: http://www.sensi.org/~ak/
+D: USB CDC OBEX driver author/maintainer
+S: Mechelininkatu 1c A11
+S: 00180 Helsinki
+S: Finland
+
 N: Mitsuru Kanda
 E: [EMAIL PROTECTED]
 E: [EMAIL PROTECTED]
diff -x sysfs.c -uNr linux-2.6.8-1.521/Documentation/usb/obex.txt 
linux-2.6.8-1.521-ak/Documentation/usb/obex.txt
--- linux-2.6.8-1.521/Documentation/usb/obex.txt        1970-01-01 02:00:00.000000000 
+0200
+++ linux-2.6.8-1.521-ak/Documentation/usb/obex.txt     2004-09-25 17:08:40.000000000 
+0300
@@ -0,0 +1,166 @@
+Linux CDC OBEX d3river v0.1
+(c) 2004 Alexander Kanavin <[EMAIL PROTECTED]>
+
+
+0. Introduction
+
+The drivers/usb/class/cdc-obex.c driver works with devices that conform to
+the Universal Serial Bus Communication Device Class Wireless Mobile
+Communication Devices Object Exchange interface (USB CDC WMC OBEX for short :). 
+The specification is available at http://www.usb.org/developers/devclass_docs
+The devices which support this are typically post-2003 mobile phones with a USB
+interface. The list of contributors to the specification includes Ericsson,
+Motorola, NEC, Nokia, NTT DoCoMo and others. The only device which has been
+confirmed to work so far is my Nokia 7610 phone, and of course I'd like to 
+hear from you about other gadgets.
+
+The driver is called cdc-obex, but you don't need to know that, because it
+should be loaded automagically when you connect your USB-compatible phone to
+your computer. If that does not happen, then maybe it's because there are
+no OBEX interfaces on your phone; lsusb -v should tell the truth.
+
+Userspace software can communicate with the device through obex device files;
+where they are depends on your distribution. /sys/class/usb/obexN/dev file 
+will tell you the major:minor pair for the device file; you can create the 
+file yourself, if it doesn't exist for some reason.
+
+1. What is OBEX?
+
+OBEX can be best described as a binary HTTP. It's a standard from "Infrared
+Data Assosiation" which includes Microsoft, Apple, Hewlett-Packard, Nokia and
+others. It's also used over Bluetooth and USB. Don't ask me why the infrared
+people had to make their own standard and not just use HTTP, because I don't 
+know. Anyway, the specification is available at http://www.irda.org and a GPLd
+implementation is at http://openobex.sf.net
+
+OBEX can be used to do a lot of things, but by default it support just two 
+of them: pushing objects to the device and querying the device about what else
+it can do. What happens to pushed objects depends on the device, typically
+they show up in some sort of 'incoming' folder.
+
+Querying the device about its capabilities works like this: after 
+connecting with default parameters you issue a GET command with 
+"x-obex/capability" MIME type. The gadget then sends you an XML file which
+contains information about the device, and what kinds of services it supports
+over OBEX. The DTD for that file is available in the OBEX specification, and
+here's what Nokia 7610 sends:
+
+<?xml version="1.0" ?>
+<!-- OBEX Capability Object -->
+<!DOCTYPE Capability SYSTEM "obex-capability.dtd">
+<Capability Version="1.0">
+  <General>
+    <Manufacturer>Nokia</Manufacturer>
+    <Model>RH-51</Model>
+    <SN>xxxxxxxxxxxxxxx</SN>  // privacy :)
+    <SW Version="V 4.0421.4" Date="20040522T000000Z"/>
+    <Language>en</Language>
+    <Memory>
+      <MemType>DEV</MemType>
+      <Location>C:\</Location>
+      <Free>6841470</Free>
+      <Used>1409922</Used>
+      <FileNLen>256</FileNLen>
+    </Memory>
+    <Memory>
+      <MemType>MMC</MemType>
+      <Location>E:\</Location>
+      <Free>43057152</Free>
+      <Used>20703232</Used>
+      <FileNLen>256</FileNLen>
+    </Memory>
+  </General>
+
+  <Service>
+    <Name>SyncML</Name>
+    <UUID>SYNCML-SYNC</UUID>
+    <Version>1.1</Version>
+    <Object>
+      <Type>application/vnd.syncml+wbxml</Type>
+    </Object>
+  </Service>
+
+  <Service>
+    <Name>PCCS</Name>
+    <UUID>F9EC7BC4-953c-11d2-984E-525400DC9E09</UUID>
+    <Version>1.0</Version>
+    <Ext>
+      <XNam>Backup</XNam>
+      <XVal>File=C:\System\backup.xml</XVal>
+    </Ext>
+  </Service>
+
+  <Service>
+    <Name>Folder-Browsing</Name>
+    <UUID>F9EC7BC4-953c-11d2-984E-525400DC9E09</UUID>
+    <Version>1.0</Version>
+    <Object>
+      <Type>x-obex/folder-listing</Type>
+    </Object>
+    <Ext>
+      <XNam>Images</XNam>
+      <XVal>Folder=C:\Nokia\Images\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+    <Ext>
+      <XNam>Graphics</XNam>
+      <XVal>Folder=C:\Nokia\Images\Backgrounds\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+    <Ext>
+      <XNam>Tones</XNam>
+      <XVal>Folder=C:\Nokia\Sounds\Digital\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+    <Ext>
+      <XNam>Music</XNam>
+      <XVal>Folder=C:\Nokia\Sounds\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+    <Ext>
+      <XNam>Videos</XNam>
+      <XVal>Folder=C:\Nokia\Videos\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+    <Ext>
+      <XNam>Installs</XNam>
+      <XVal>Folder=C:\Nokia\Installs\</XVal>
+      <XVal>MemType=DEV</XVal>
+    </Ext>
+  </Service>
+
+</Capability>
+
+Neat, isn't it? You don't even need the DTD to make sense of that. So we can
+talk in SyncML to exchange contacts/calendar/notes and perhaps other things,
+browse the phone filesystem (the phone even tells where the photos and videos 
+you have taken are), and do backups based on the information in that backup.xml
+file. SyncML over OBEX is documented at http://www.syncml.org, and filebrowsing
+service is described in the OBEX specification itself.
+
+2. How to use OBEX.
+
+Basically, you should use openobex library, it's quite well written.
+Openobex supports several transports, the one you should use to access
+interfaces provided by this driver is the "custom" transport, where you 
+supply hook functions for reading and writing data. Look at obex_test 
+example application from openobex-apps package for an example.
+
+IMPORTANT! Nokia 7610 has a bug: it doesn't support packet sizes lesser than
+about 2 KBytes, and openobex has a default size of 1K. When you try to use 
+that, the phone simply does not respond. The workaround is to use 
+OBEX_SetTransportMTU function to set the packet size to something bigger. 
+A good choice is the maximum packet size which the device supports. It's 
+returned in the response to the CONNECT command, and in the case of Nokia 7610
+it's 4000 bytes.
+
+3. Troubleshooting
+
+Please note that I most likely won't be able to do anything about bugs in the
+device OBEX protocol implementation and protocols/data formats that work on
+top of it. This driver only deals with the USB transport mechanism. 
+
+When you send bug reports, in additional to the usual problem description that
+makes sense, and exact error messages, also attach lsusb -v output. Asking
+on the linux-usb-users maling list is a good idea too, because if I don't know
+the answer, other people might.
\ В конце файла нет новой строки
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/class/cdc-acm.c 
linux-2.6.8-1.521-ak/drivers/usb/class/cdc-acm.c
--- linux-2.6.8-1.521/drivers/usb/class/cdc-acm.c       2004-08-14 08:37:15.000000000 
+0300
+++ linux-2.6.8-1.521-ak/drivers/usb/class/cdc-acm.c    2004-09-25 17:12:04.702228974 
+0300
@@ -541,7 +541,7 @@
        u8 call_management_function = 0;
        int call_interface_num = -1;
        int data_interface_num;
-
+       
        if (!buffer) {
                err("Wierd descriptor references");
                return -EINVAL;
@@ -563,6 +563,8 @@
                                break;
                        case CDC_COUNTRY_TYPE: /* maybe somehow export */
                                break; /* for now we ignore it */
+                        case CDC_HEADER_TYPE: /* maybe check version */
+                                break; /* for now we ignore it */
                        case CDC_AC_MANAGEMENT_TYPE:
                                ac_management_function = buffer[3];
                                break;
@@ -574,7 +576,7 @@
                                break;
                                
                        default:
-                               err("Ignoring extra header");
+                               err("Ignoring extra header, type %d, length %d", 
buffer[2], buffer[0]);
                                break;
                        }
 next_desc:
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/class/cdc-acm.h 
linux-2.6.8-1.521-ak/drivers/usb/class/cdc-acm.h
--- linux-2.6.8-1.521/drivers/usb/class/cdc-acm.h       2004-08-14 08:38:09.000000000 
+0300
+++ linux-2.6.8-1.521-ak/drivers/usb/class/cdc-acm.h    2004-09-22 01:07:03.000000000 
+0300
@@ -117,6 +117,7 @@
 } __attribute__ ((packed));
 
 /* class specific descriptor types */
+#define CDC_HEADER_TYPE                        0x00
 #define CDC_CALL_MANAGEMENT_TYPE       0x01
 #define CDC_AC_MANAGEMENT_TYPE         0x02
 #define CDC_UNION_TYPE                 0x06
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/class/cdc-obex.c 
linux-2.6.8-1.521-ak/drivers/usb/class/cdc-obex.c
--- linux-2.6.8-1.521/drivers/usb/class/cdc-obex.c      1970-01-01 02:00:00.000000000 
+0200
+++ linux-2.6.8-1.521-ak/drivers/usb/class/cdc-obex.c   2004-09-25 15:29:36.000000000 
+0300
@@ -0,0 +1,770 @@
+/*
+ * USB CDC OBEX driver - 0.1
+ *
+ * Copyright (C) 2004 Alexander Kanavin ([EMAIL PROTECTED])
+ * Uses portions of USB skeleton driver by Greg Kroah-Hartman
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation, version 2.
+ *
+ *
+ * USB OBEX driver for mobile devices with an USB interface
+ * (such as post-2003 Nokia phones).
+ * Please see Documentation/usb/obex.txt for details.
+ *
+ * History:
+ *
+ * 25 Sep 2004 - 0.1 - first version
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+#include "cdc-acm.h"
+
+#ifdef CONFIG_USB_DEBUG
+       static int debug = 1;
+#else
+       static int debug;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format 
"\n" , ## arg); } while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Alexander Kanavin, [EMAIL PROTECTED]"
+#define DRIVER_DESC "USB OBEX Driver"
+
+/* Module parameters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+/* table of devices that work with this driver */
+static struct usb_device_id obex_table [] = {
+       { USB_INTERFACE_INFO(USB_CLASS_COMM, 11, 0) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, obex_table);
+
+/* OBEX interface descriptor constant */
+#define CDC_OBEX_TYPE          0x15
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_OBEX_MINOR_BASE    192
+
+/* Structure to hold all of our device specific stuff */
+struct usb_obex {
+       struct usb_device *     udev;                   /* save off the usb device 
pointer */
+       struct usb_interface *  interface;              /* the interface for this 
device */
+       struct usb_interface *  data_interface;                 /* the data interface 
for this device */
+       unsigned char           minor;                  /* the starting minor number 
for this device */
+       unsigned char           num_ports;              /* the number of ports this 
device has */
+       char                    num_interrupt_in;       /* number of interrupt in 
endpoints we have */
+       char                    num_bulk_in;            /* number of bulk in endpoints 
we have */
+       char                    num_bulk_out;           /* number of bulk out 
endpoints we have */
+
+       unsigned char *         bulk_in_buffer;         /* the buffer to receive data 
*/
+       size_t                  bulk_in_size;           /* the size of the receive 
buffer */
+       __u8                    bulk_in_endpointAddr;   /* the address of the bulk in 
endpoint */
+       
+       unsigned char *         bulk_out_buffer;        /* the buffer to send data */
+       size_t                  bulk_out_size;          /* the size of the send buffer 
*/
+       struct urb *            write_urb;              /* the urb used to send data */
+       __u8                    bulk_out_endpointAddr;  /* the address of the bulk out 
endpoint */
+       dma_addr_t              write_dma;              /* dma handler of write buffer 
*/
+       atomic_t                write_busy;             /* true if write urb is busy */
+       struct completion       write_finished;         /* wait for the write to 
finish */
+
+       int                     open;                   /* if the port is open or not 
*/
+       int                     present;                /* if the device is not 
disconnected */
+       struct semaphore        sem;                    /* locks this structure */
+};
+
+
+/* prevent races between open() and disconnect() */
+static DECLARE_MUTEX (disconnect_sem);
+
+/* local function prototypes */
+static ssize_t obex_read       (struct file *file, char *buffer, size_t count, loff_t 
*ppos);
+static ssize_t obex_write      (struct file *file, const char *buffer, size_t count, 
loff_t *ppos);
+static int obex_open           (struct inode *inode, struct file *file);
+static int obex_release                (struct inode *inode, struct file *file);
+
+static int obex_probe          (struct usb_interface *interface, const struct 
usb_device_id *id);
+static void obex_disconnect    (struct usb_interface *interface);
+
+static void obex_write_bulk_callback   (struct urb *urb, struct pt_regs *regs);
+
+/*
+ * File operations needed when we register this driver.
+ */
+static struct file_operations obex_fops = {
+       .owner =        THIS_MODULE,
+
+       .read =         obex_read,
+       .write =        obex_write,
+       .open =         obex_open,
+       .release =      obex_release,
+};
+
+/* 
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver obex_class = {
+       .name =         "usb/obex%d",
+       .fops =         &obex_fops,
+       .mode =         S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+       .minor_base =   USB_OBEX_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver obex_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "cdc_obex",
+       .probe =        obex_probe,
+       .disconnect =   obex_disconnect,
+       .id_table =     obex_table,
+};
+
+
+/**
+ *     usb_obex_debug_data
+ */
+static inline void usb_obex_debug_data (const char *function, int size, const 
unsigned char *data)
+{
+       int i;
+
+       if (!debug)
+               return;
+
+       printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
+               function, size);
+       for (i = 0; i < size; ++i) {
+               printk ("%.2x ", data[i]);
+       }
+       printk ("\n");
+}
+
+/**
+ *     obex_open_data_interface
+ *     
+ *     All OBEX devices have two alternative settings for an OBEX 
+ *     data interface. The first one is an "idle" setting and it 
+ *     has no data endpoints, the second one is an "active" setting and 
+ *     it has two bulk endpoints for input and output. Here we search
+ *     for the second setting, activate it and set it up.
+ */
+static inline int obex_open_data_interface (struct usb_obex *dev)
+{
+       int i;
+       int retval = -ENOMEM;
+       struct usb_host_interface *iface_desc;
+        struct usb_endpoint_descriptor *endpoint;
+        size_t buffer_size;
+        struct usb_device *udev = interface_to_usbdev(dev->data_interface);
+
+       /* choose correct interface setting */
+       for (i = 0; i < dev->data_interface->num_altsetting; i++) {
+               iface_desc = &dev->data_interface->altsetting[i];
+               if (iface_desc->desc.bNumEndpoints == 2)
+                       goto found;
+       }
+       retval = -ENODEV;
+       err("interface setting with 2 endpoints not found");
+       goto error;
+found:
+       retval = usb_set_interface (dev->udev, iface_desc->desc.bInterfaceNumber,
+                                iface_desc->desc.bAlternateSetting);
+        if (retval) {
+               err("usb_set_interface error: %d", retval);
+                goto error;
+       }
+
+       /* set up the endpoint information */
+       /* check out the endpoints */
+       /* use only the first bulk-in and bulk-out endpoints */
+       iface_desc = dev->data_interface->cur_altsetting;
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (!dev->bulk_in_endpointAddr &&
+                   (endpoint->bEndpointAddress & USB_DIR_IN) &&
+                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                                       == USB_ENDPOINT_XFER_BULK)) {
+                       buffer_size = endpoint->wMaxPacketSize;
+                       dev->bulk_in_size = buffer_size;
+                       dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+               }
+               
+               if (!dev->bulk_out_endpointAddr &&
+                   !(endpoint->bEndpointAddress & USB_DIR_IN) &&
+                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                                       == USB_ENDPOINT_XFER_BULK)) {
+                       dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+
+                       /* on some platforms using this kind of buffer alloc
+                        * call eliminates a dma "bounce buffer".
+                        *
+                        * NOTE: you'd normally want i/o buffers that hold
+                        * more than one packet, so that i/o delays between
+                        * packets don't hurt throughput.
+                        */
+                       buffer_size = endpoint->wMaxPacketSize;
+                       dev->bulk_out_size = buffer_size;
+               }
+       }
+       if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+               err("Couldn't find both bulk-in and bulk-out endpoints");
+               goto error;
+       }
+       /* Set up buffers and urbs */
+       dev->bulk_in_buffer = kmalloc (dev->bulk_in_size, GFP_KERNEL);
+       if (!dev->bulk_in_buffer) {
+               err("Couldn't allocate bulk_in_buffer");
+               goto error;
+       }
+       dev->bulk_out_buffer = usb_buffer_alloc (udev, dev->bulk_out_size, GFP_KERNEL, 
&dev->write_dma);
+       if (!dev->bulk_out_buffer) {
+               err("Couldn't allocate bulk_out_buffer");
+               goto alloc_fail1;
+       }
+       dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->write_urb) {
+               err("No free urbs available");
+               goto alloc_fail2;
+       }
+
+       usb_fill_bulk_urb(dev->write_urb, udev, usb_sndbulkpipe(udev, 
dev->bulk_out_endpointAddr),
+                         dev->bulk_out_buffer, dev->bulk_out_size, 
obex_write_bulk_callback, dev);
+       dev->write_urb->transfer_flags |= URB_ASYNC_UNLINK | URB_NO_TRANSFER_DMA_MAP;
+       dev->write_urb->transfer_dma = dev->write_dma;
+
+        return 0;
+
+alloc_fail2:
+       usb_buffer_free(udev, dev->bulk_out_size, dev->bulk_out_buffer, 
dev->write_dma);
+alloc_fail1:
+       kfree(dev->bulk_in_buffer);
+error:
+        return retval;
+}
+
+/**
+ *     obex_close_data_interface
+ *
+ *     Accordingly this function switches to the "idle" setting, and frees
+ *     the related data structures.
+ */
+
+static inline void obex_close_data_interface (struct usb_obex *dev)
+{
+       int i;
+       struct usb_host_interface *alt;
+
+       kfree (dev->bulk_in_buffer);
+       usb_buffer_free (dev->udev, dev->bulk_out_size,
+                               dev->bulk_out_buffer,
+                               dev->write_urb->transfer_dma);
+       usb_free_urb (dev->write_urb);
+        /* choose correct interface setting */
+       for (i = 0; i < dev->data_interface->num_altsetting; i++) {
+               alt = &dev->data_interface->altsetting[i];
+               if (alt->desc.bNumEndpoints == 0)
+                       usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+                                          alt->desc.bAlternateSetting);
+       }
+}
+
+/**
+ *     obex_open
+ *
+ *     This is called when an application opens a device file. Note that
+ *     multiple opens are not supported; it would not make sense anyway, 
+ *     because if the applications start making simultaneous requests, it 
+ *     would confuse the OBEX engine on the device. If you need to do that,
+ *     write a spooler.
+ */
+static int obex_open (struct inode *inode, struct file *file) 
+{
+       struct usb_obex *dev = NULL;
+       struct usb_interface *interface;
+       int subminor;
+       int retval = 0;
+
+       dbg("%s", __FUNCTION__);
+
+       subminor = iminor(inode);
+
+       /* prevent disconnects */
+       down (&disconnect_sem);
+
+       interface = usb_find_interface (&obex_driver, subminor);
+       if (!interface) {
+               err ("%s - error, can't find device for minor %d",
+                    __FUNCTION__, subminor);
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       /* lock this device */
+       down (&dev->sem);
+
+       if (!dev->open) {
+               retval = obex_open_data_interface(dev);
+               if (retval)
+                       goto exit_no_interface;
+               /* increment our usage count for the driver */
+               ++dev->open;
+
+               /* save our object in the file's private structure */
+               
+               file->private_data = dev;
+       } else {
+               retval = -EBUSY;
+       }
+
+exit_no_interface:
+       /* unlock this device */
+       up (&dev->sem);
+
+exit_no_device:
+       up (&disconnect_sem);
+       return retval;
+}
+
+
+/**
+ *     obex_release
+ *
+ *     This is called when an application closes the device filele.
+ *     Note that the device may have been unplugged by then already, 
+ *     in which case we free the dev structure to avoid memory leaks.
+ */
+static int obex_release (struct inode *inode, struct file *file)
+{
+       struct usb_obex *dev;
+       int retval = 0;
+
+       dev = (struct usb_obex *)file->private_data;
+       if (dev == NULL) {
+               dbg ("%s - object is NULL", __FUNCTION__);
+               return -ENODEV;
+       }
+
+       dbg("%s - minor %d", __FUNCTION__, dev->minor);
+
+       /* lock our device */
+       down (&dev->sem);
+
+       if (dev->open <= 0) {
+               dbg ("%s - device not opened", __FUNCTION__);
+               retval = -ENODEV;
+               goto exit_not_opened;
+       }
+
+       /* wait for any bulk writes that might be going on to finish up */
+       if (atomic_read (&dev->write_busy))
+               wait_for_completion (&dev->write_finished);
+
+       --dev->open;
+
+        /* prevent device read, write and ioctl */
+       if (dev->present)
+               obex_close_data_interface(dev);
+       else
+               kfree(dev);
+       up (&dev->sem);
+       return 0;
+
+exit_not_opened:
+       up (&dev->sem);
+
+       return retval;
+}
+
+
+/**
+ *     obex_read
+ *
+ *     Blocking call that reads from the device the maximum amount of bytes
+ *     that the endpoint supports. If there's nothing to read, the call 
+ *     timeouts with an error after a few seconds. OBEX is a request/response
+ *     protocol, so you're only supposed to read from the device, when you
+ *     know there must be a response. If there isn't then the device is
+ *     buggy.
+ */
+static ssize_t obex_read (struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+       struct usb_obex *dev;
+       int retval = 0;
+
+       dev = (struct usb_obex *)file->private_data;
+
+       dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device is set up and plugged */
+       if (!dev->present || !dev->open) {
+               up (&dev->sem);
+               return -ENODEV;
+       }
+
+       /* do a blocking bulk read to get data from the device */
+       retval = usb_bulk_msg (dev->udev,
+                              usb_rcvbulkpipe (dev->udev,
+                                               dev->bulk_in_endpointAddr),
+                              dev->bulk_in_buffer,
+                              min (dev->bulk_in_size, count),
+                              &count, HZ*10);
+
+       /* if the read was successful, copy the data to userspace */
+       if (!retval) {
+               if (copy_to_user (buffer, dev->bulk_in_buffer, count))
+                       retval = -EFAULT;
+               else
+                       retval = count;
+       }
+
+       /* unlock the device */
+       up (&dev->sem);
+       return retval;
+}
+
+
+/**
+ *     obex_write
+ *
+ *     This writes the data to a device. The typical packet size on a bulk
+ *     endpoint is rather small, 64 bytes or something like that, so we're
+ *     writing all of user-supplied data in a loop, and not just the 
+ *     first chunk. Another reason is that openobex userspace library expects
+ *     the transport to support packets sizes defined by the OBEX standard, 
+ *     and the minimum which the device has to support is 255 bytes. 
+ *     If you need to transfer large items, then CONNECT response from a 
+ *     device contains the maximum packet size, use it for your transfers.
+ *     No double-buffering to keep things simple.
+ *
+ *     A device driver has to decide how to report I/O errors back to the
+ *     user.  The safest course is to wait for the transfer to finish before
+ *     returning so that any errors will be reported reliably.  obex_read()
+ *     works like this.  But waiting for I/O is slow, so many drivers only
+ *     check for errors during I/O initiation and do not report problems
+ *     that occur during the actual transfer.  That's what we will do here.
+ *
+ */
+static ssize_t obex_write (struct file *file, const char *buffer, size_t count, 
loff_t *ppos)
+{
+       struct usb_obex *dev;
+       ssize_t bytes_written = 0;
+       ssize_t bytes_to_write = 0;
+       int retval = 0;
+
+       dev = (struct usb_obex *)file->private_data;
+
+       dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (!dev->present || !dev->open) {
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* verify that we actually have some data to write */
+       if (count == 0) {
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               goto exit;
+       }
+
+       while (bytes_written < count) {
+               /* wait for a previous write to finish up; we don't use a timeout
+                * and so a nonresponsive device can delay us indefinitely.
+                */
+               if (atomic_read (&dev->write_busy))
+                       wait_for_completion (&dev->write_finished);
+
+               /* we can only write as much as our buffer will hold */
+               bytes_to_write = min (dev->bulk_out_size, count - bytes_written);
+
+               /* copy the data from userspace into our transfer buffer;
+                * this is the only copy required.
+                */
+               if (copy_from_user(dev->write_urb->transfer_buffer, 
buffer+bytes_written,
+                                   bytes_to_write)) {
+                       retval = -EFAULT;
+                       goto exit;
+               }
+               bytes_written += bytes_to_write;
+
+               usb_obex_debug_data (__FUNCTION__, bytes_written,
+                                    dev->write_urb->transfer_buffer);
+
+               /* this urb was already set up, except for this write size */
+               dev->write_urb->transfer_buffer_length = bytes_to_write;
+
+               /* send the data out the bulk port */
+               /* a character device write uses GFP_KERNEL,
+                  unless a spinlock is held */
+               init_completion (&dev->write_finished);
+               atomic_set (&dev->write_busy, 1);
+               retval = usb_submit_urb(dev->write_urb, GFP_KERNEL);
+               if (retval) {
+                       atomic_set (&dev->write_busy, 0);
+                       err("%s - failed submitting write urb, error %d",
+                           __FUNCTION__, retval);
+                       goto exit;
+               } else {
+                       retval = bytes_written;
+               }
+       }
+exit:
+       /* unlock the device */
+       up (&dev->sem);
+
+       return retval;
+}
+
+
+/**
+ *     obex_write_bulk_callback
+ *     
+ *     Writes are asynchronous, so this function is called when they're over.
+ */
+static void obex_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_obex *dev = (struct usb_obex *)urb->context;
+
+       dbg("%s - minor %d", __FUNCTION__, dev->minor);
+
+       /* sync/async unlink faults aren't errors */
+       if (urb->status && !(urb->status == -ENOENT ||
+                               urb->status == -ECONNRESET)) {
+               dbg("%s - nonzero write bulk status received: %d",
+                   __FUNCTION__, urb->status);
+       }
+
+       /* notify anyone waiting that the write has finished */
+       atomic_set (&dev->write_busy, 0);
+       complete (&dev->write_finished);
+}
+
+
+/**
+ *     obex_probe
+ *
+ *     Called by the usb core when a new device is connected that it thinks
+ *     this driver might be interested in. CDC OBEX devices have two 
+ *     interfaces: a master interface which is basically useless except it
+ *     can contain a textual description of itself, and a slave interface 
+ *     which contains the actual data endpoints. Here, we search for the 
+ *     latter, but don't set it up until the device file is actually opened.
+ */
+static int obex_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+        struct union_desc *union_header = NULL;
+        char *buffer = interface->altsetting->extra;
+        int buflen = interface->altsetting->extralen;
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct usb_obex *dev = NULL;
+        struct usb_interface *data_interface;
+        int data_interface_num;
+        int retval = -ENOMEM;
+
+       /* allocate memory for our device state and initialize it */
+       dev = kmalloc (sizeof(struct usb_obex), GFP_KERNEL);
+       if (dev == NULL) {
+               err ("Out of memory");
+               return -ENOMEM;
+       }
+       memset (dev, 0x00, sizeof (*dev));
+
+       init_MUTEX (&dev->sem);
+       dev->udev = udev;
+       dev->interface = interface;
+
+        if (!buffer) {
+                err("Weird descriptor references");
+                return -EINVAL;
+        }
+
+        while (buflen > 0) {
+                if (buffer [1] != USB_DT_CS_INTERFACE) {
+                        err("skipping garbage");
+                        goto next_desc;
+                }
+
+                switch (buffer [2]) {
+                        case CDC_UNION_TYPE: /* we've found it */
+                                if (union_header) {
+                                        err("More than one union descriptor, skipping 
...");
+                                        goto next_desc;
+                                }
+                                union_header = (struct union_desc *)buffer;
+                                break;
+                        case CDC_OBEX_TYPE: /* maybe check version */
+                       case CDC_HEADER_TYPE:
+                                break; /* for now we ignore it */
+                        default:
+                                err("Ignoring extra header, type %d, length %d", 
buffer[2], buffer[0]);
+                                break;
+                        }
+next_desc:
+                buflen -= buffer[0];
+                buffer += buffer[0];
+        }
+
+        if (!union_header) {
+                dev_dbg(&interface->dev,"No union descriptor, giving up\n");
+                return -ENODEV;
+        }
+       /* Found the slave interface */
+       data_interface = usb_ifnum_to_if(udev, (data_interface_num = 
union_header->bSlaveInterface0));
+
+        if (!data_interface) {
+                dev_dbg(&interface->dev,"no interfaces\n");
+                return -ENODEV;
+        }
+
+        if (usb_interface_claimed(data_interface)) { /* valid in this context */
+                dev_dbg(&interface->dev,"The data interface isn't available\n");
+                return -EBUSY;
+        }
+
+       dev->data_interface = data_interface;
+       /* we can register the device now, as it is ready */
+       usb_driver_claim_interface(&obex_driver, data_interface, dev);
+       usb_set_intfdata (interface, dev);
+       retval = usb_register_dev (interface, &obex_class);
+       if (retval) {
+               /* something prevented us from registering this driver */
+               err ("Not able to get a minor for this device.");
+               usb_set_intfdata (interface, NULL);
+               goto error;
+       }
+
+       dev->minor = interface->minor;
+       dev->present = 1;
+
+       /* let the user know what node this device is now attached to */
+       info ("USB OBEX device now attached to USBOBEX-%d", dev->minor);
+       return 0;
+
+error:
+       return retval;
+}
+
+
+/**
+ *     obex_disconnect
+ *
+ *     Called by the usb core when the device is removed from the system. 
+ *     It frees all the related data structures. However, the device file
+ *     might be still open, so the dev structure is not freed in that case,
+ *     or the driver will oops, when that file is closed.
+ *
+ *     This routine guarantees that the driver will not submit any more urbs
+ *     by clearing dev->udev.  It is also supposed to terminate any currently
+ *     active urbs.  Unfortunately, usb_bulk_msg(), used in obex_read(), does
+ *     not provide any way to do this.  But at least we can cancel an active
+ *     write.
+ */
+static void obex_disconnect(struct usb_interface *interface)
+{
+       struct usb_obex *dev;
+       int minor;
+
+       dev = usb_get_intfdata (interface);
+       if (interface == dev->data_interface)
+               return;
+       /* prevent races with open() */
+       down (&disconnect_sem);
+       usb_set_intfdata (interface, NULL);
+       down (&dev->sem);
+       minor = dev->minor;
+
+       /* give back our minor */
+       usb_deregister_dev (interface, &obex_class);
+       
+       if (dev->open) {
+               /* terminate an ongoing write */
+               if (atomic_read (&dev->write_busy)) {
+                       usb_unlink_urb (dev->write_urb);
+                       wait_for_completion (&dev->write_finished);
+               }
+               obex_close_data_interface(dev);
+       }
+        usb_driver_release_interface(&obex_driver, dev->data_interface);
+       dev->present = 0;
+       up (&dev->sem);
+       
+       if (!dev->open)
+               kfree (dev);
+       up (&disconnect_sem);
+
+       info("USB OBEX #%d now disconnected", minor);
+}
+
+
+
+/**
+ *     usb_obex_init
+ */
+static int __init usb_obex_init(void)
+{
+       int result;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&obex_driver);
+       if (result) {
+               err("usb_register failed. Error number %d",
+                   result);
+               return result;
+       }
+
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+}
+
+
+/**
+ *     usb_obex_exit
+ */
+static void __exit usb_obex_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&obex_driver);
+}
+
+
+module_init (usb_obex_init);
+module_exit (usb_obex_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/class/Kconfig 
linux-2.6.8-1.521-ak/drivers/usb/class/Kconfig
--- linux-2.6.8-1.521/drivers/usb/class/Kconfig 2004-08-14 08:36:13.000000000 +0300
+++ linux-2.6.8-1.521-ak/drivers/usb/class/Kconfig      2004-09-25 15:03:41.000000000 
+0300
@@ -73,6 +73,19 @@
          To compile this driver as a module, choose M here: the
          module will be called cdc-acm.
 
+config USB_OBEX
+       tristate "USB OBject EXchange (CDC OBEX) support"
+       depends on USB
+       ---help---
+         This driver support mobile phones with USB which support the
+         Communication Device Class OBEX interface. This typically allows
+         read/write access to the phone filesystem, and possibly also
+         phonebook, calendar and notes. Please read <file:Documentation/usb/obex.txt>
+         for details.
+         
+         To compile this driver as a module choose M here: the module will be 
+         called cdc-obex.
+         
 config USB_PRINTER
        tristate "USB Printer support"
        depends on USB
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/class/Makefile 
linux-2.6.8-1.521-ak/drivers/usb/class/Makefile
--- linux-2.6.8-1.521/drivers/usb/class/Makefile        2004-08-14 08:36:57.000000000 
+0300
+++ linux-2.6.8-1.521-ak/drivers/usb/class/Makefile     2004-09-21 23:50:33.000000000 
+0300
@@ -7,4 +7,5 @@
 obj-$(CONFIG_USB_AUDIO)                += audio.o
 obj-$(CONFIG_USB_BLUETOOTH_TTY)        += bluetty.o
 obj-$(CONFIG_USB_MIDI)         += usb-midi.o
+obj-$(CONFIG_USB_OBEX)         += cdc-obex.o
 obj-$(CONFIG_USB_PRINTER)      += usblp.o
diff -x sysfs.c -uNr linux-2.6.8-1.521/drivers/usb/Makefile 
linux-2.6.8-1.521-ak/drivers/usb/Makefile
--- linux-2.6.8-1.521/drivers/usb/Makefile      2004-08-14 08:38:04.000000000 +0300
+++ linux-2.6.8-1.521-ak/drivers/usb/Makefile   2004-09-21 23:50:28.000000000 +0300
@@ -15,6 +15,7 @@
 obj-$(CONFIG_USB_AUDIO)                += class/
 obj-$(CONFIG_USB_BLUETOOTH_TTY)        += class/
 obj-$(CONFIG_USB_MIDI)         += class/
+obj-$(CONFIG_USB_OBEX)         += class/
 obj-$(CONFIG_USB_PRINTER)      += class/
 
 obj-$(CONFIG_USB_STORAGE)      += storage/
diff -x sysfs.c -uNr linux-2.6.8-1.521/MAINTAINERS linux-2.6.8-1.521-ak/MAINTAINERS
--- linux-2.6.8-1.521/MAINTAINERS       2004-08-14 08:37:15.000000000 +0300
+++ linux-2.6.8-1.521-ak/MAINTAINERS    2004-09-25 15:16:19.000000000 +0300
@@ -2159,6 +2159,14 @@
 S:     Maintained
 W:     http://www.kroah.com/linux-usb/
 
+USB CDC OBEX DRIVER
+P:     Alexander Kanavin
+M:     [EMAIL PROTECTED]
+L:     [EMAIL PROTECTED]
+L:     [EMAIL PROTECTED]
+S:     Maintained
+W:     http://www.sensi.org/~ak/
+
 USB EHCI DRIVER
 P:     David Brownell
 M:     [EMAIL PROTECTED]




-------------------------------------------------------
This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170
Project Admins to receive an Apple iPod Mini FREE for your judgement on
who ports your project to Linux PPC the best. Sponsored by IBM.
Deadline: Sept. 24. Go here: http://sf.net/ppc_contest.php
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to