ChangeSet 1.1006.11.5, 2003/03/11 17:45:38-08:00, [EMAIL PROTECTED]
[PATCH] USB: add support for Treo devices to the visor driver
Thanks to Adam Pennington <[EMAIL PROTECTED]> for the bulk of this work.
drivers/usb/serial/visor.c | 227 +++++++++++++++++++++++++++++++++++++--------
drivers/usb/serial/visor.h | 37 ++++++-
2 files changed, 223 insertions(+), 41 deletions(-)
diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
--- a/drivers/usb/serial/visor.c Thu Mar 27 16:02:35 2003
+++ b/drivers/usb/serial/visor.c Thu Mar 27 16:02:35 2003
@@ -2,7 +2,7 @@
* USB HandSpring Visor, Palm m50x, and Sony Clie driver
* (supports all of the Palm OS USB devices)
*
- * Copyright (C) 1999 - 2002
+ * Copyright (C) 1999 - 2003
* Greg Kroah-Hartman ([EMAIL PROTECTED])
*
* This program is free software; you can redistribute it and/or modify
@@ -16,6 +16,16 @@
* Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl
* <[EMAIL PROTECTED]> for the information.
*
+ * (3/07/2003) Adam Pennington <[EMAIL PROTECTED]>
+ * Backported version 2.1 of the driver from the 2.5 bitkeeper tree
+ * making Treo actually work.
+ *
+ * (2/18/2003) Adam Powell <hazelsct at debian.org>
+ * Backported 2.5 driver mods to support Handspring Treo.
+ *
+ * (2/11/2003) Adam Powell <hazelsct at debian.org>
+ * Added device and vendor ids for the Samsung I330 phone.
+ *
* (04/03/2002) gkh
* Added support for the Sony OS 4.1 devices. Thanks to Hiroyuki ARAKI
* <[EMAIL PROTECTED]> for the information.
@@ -150,9 +160,9 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.6"
+#define DRIVER_VERSION "v1.7"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <[EMAIL PROTECTED]>"
-#define DRIVER_DESC "USB HandSpring Visor, Palm m50x, Sony Cli� driver"
+#define DRIVER_DESC "USB HandSpring Visor, Palm m50x, Treo, Sony Cli� driver"
/* function prototypes for a handspring visor */
static int visor_open (struct usb_serial_port *port, struct file *filp);
@@ -168,10 +178,14 @@
static void visor_set_termios (struct usb_serial_port *port, struct termios
*old_termios);
static void visor_write_bulk_callback (struct urb *urb);
static void visor_read_bulk_callback (struct urb *urb);
+static void visor_read_int_callback (struct urb *urb);
static int clie_3_5_startup (struct usb_serial *serial);
+static void treo_attach (struct usb_serial *serial);
static struct usb_device_id id_table [] = {
+ { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
+ { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
@@ -182,12 +196,12 @@
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
- { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
+ { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
{ } /* Terminating entry */
};
@@ -198,6 +212,7 @@
static __devinitdata struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
+ { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
@@ -214,6 +229,7 @@
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
+ { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
{ } /* Terminating entry */
};
@@ -224,9 +240,9 @@
/* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */
static struct usb_serial_device_type handspring_device = {
.owner = THIS_MODULE,
- .name = "Handspring Visor / Palm 4.0 / Cli� 4.x",
+ .name = "Handspring Visor / Treo / Palm 4.0 / Cli� 4.x",
.id_table = id_table,
- .num_interrupt_in = 0,
+ .num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = 2,
.num_bulk_out = 2,
.num_ports = 2,
@@ -243,6 +259,7 @@
.chars_in_buffer = visor_chars_in_buffer,
.write_bulk_callback = visor_write_bulk_callback,
.read_bulk_callback = visor_read_bulk_callback,
+ .read_int_callback = visor_read_int_callback,
};
/* device info for the Sony Clie OS version 3.5 */
@@ -315,9 +332,19 @@
port->read_urb->transfer_buffer_length,
visor_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb);
- if (result)
+ if (result) {
err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ goto exit;
+ }
+ if (port->interrupt_in_urb) {
+ dbg("%s - adding interrupt input for treo", __FUNCTION__);
+ result = usb_submit_urb(port->interrupt_in_urb);
+ if (result)
+ err("%s - failed submitting interrupt urb, error %d\n",
+ __FUNCTION__, result);
+ }
+exit:
return result;
}
@@ -336,6 +363,9 @@
if (!serial)
return;
+
+
+
if (serial->dev) {
/* only send a shutdown message if the
* device is still here */
@@ -351,11 +381,25 @@
transfer_buffer, 0x12, 300);
kfree (transfer_buffer);
}
- /* shutdown our bulk read */
+
+ /* shutdown our urbs */
usb_unlink_urb (port->read_urb);
+ if (port->interrupt_in_urb)
+ usb_unlink_urb (port->interrupt_in_urb);
+ /* Try to send shutdown message, if the device is gone, this will just
fail. */
+ transfer_buffer = kmalloc (0x12, GFP_KERNEL);
+ if (transfer_buffer) {
+ usb_control_msg (serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ VISOR_CLOSE_NOTIFICATION, 0xc2,
+ 0x0000, 0x0000,
+ transfer_buffer, 0x12, 300);
+ kfree (transfer_buffer);
+
+ }
}
/* Uncomment the following line if you want to see some statistics in your
syslog */
- /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */
+ info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out);
}
@@ -552,6 +596,40 @@
}
+static void visor_read_int_callback (struct urb *urb)
+{
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ /*
+ * This information is still unknown what it can be used for.
+ * If anyone has an idea, please let the author know...
+ *
+ * Rumor has it this endpoint is used to notify when data
+ * is ready to be read from the bulk ones.
+ */
+ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+
+exit:
+ return;
+}
+
+
static void visor_throttle (struct usb_serial_port *port)
{
dbg("%s - port %d", __FUNCTION__, port->number);
@@ -575,30 +653,49 @@
{
int response;
int i;
- unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL);
-
- if (!transfer_buffer) {
- err("%s - kmalloc(%d) failed.", __FUNCTION__, 256);
- return -ENOMEM;
- }
+ unsigned char *transfer_buffer;
dbg("%s", __FUNCTION__);
dbg("%s - Set config to 1", __FUNCTION__);
usb_set_configuration (serial->dev, 1);
- /* send a get connection info request */
- response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
VISOR_GET_CONNECTION_INFORMATION,
- 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12,
300);
- if (response < 0) {
- err("%s - error getting connection information", __FUNCTION__);
- } else {
- struct visor_connection_info *connection_info = (struct
visor_connection_info *)transfer_buffer;
+ if ((serial->dev->descriptor.idVendor == HANDSPRING_VENDOR_ID) &&
+ (serial->dev->descriptor.idProduct == HANDSPRING_VISOR_ID)) {
+ struct visor_connection_info *connection_info;
char *string;
+ int num_ports;
+
+ transfer_buffer = kmalloc (sizeof (*connection_info),
+ GFP_KERNEL);
+ if (!transfer_buffer) {
+ err("%s - kmalloc(%d) failed.", __FUNCTION__,
+ sizeof (*connection_info));
+ return -ENOMEM;
+ }
+ /* send a get connection info request */
+ response = usb_control_msg (serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ VISOR_GET_CONNECTION_INFORMATION,
+ 0xc2, 0x0000, 0x0000,
+ transfer_buffer,
+ sizeof (*connection_info), 300);
+ if (response < 0) {
+ err("%s - error getting connection information",
+ __FUNCTION__);
+ goto exit;
+ }
+
+ connection_info = (struct visor_connection_info *)transfer_buffer;
le16_to_cpus(&connection_info->num_ports);
+ num_ports = connection_info->num_ports;
+
+ /* handle devices that report invalid stuff here */
+ if (num_ports > 2)
+ num_ports = 2;
info("%s: Number of ports: %d", serial->type->name,
connection_info->num_ports);
- for (i = 0; i < connection_info->num_ports; ++i) {
+ for (i = 0; i < num_ports; ++i) {
switch (connection_info->connections[i].port_function_id) {
case VISOR_FUNCTION_GENERIC:
string = "Generic";
@@ -622,32 +719,32 @@
info("%s: port %d, is for %s use and is bound to ttyUSB%d",
serial->type->name,
connection_info->connections[i].port, string,
serial->minor + i);
}
- }
+ } else {
+ struct palm_ext_connection_info *connection_info;
- if ((serial->dev->descriptor.idVendor == PALM_VENDOR_ID) ||
- ((serial->dev->descriptor.idVendor == SONY_VENDOR_ID) &&
- (serial->dev->descriptor.idProduct != SONY_CLIE_4_1_ID))) {
- /* Palm OS 4.0 Hack */
- response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev,
0),
- PALM_GET_SOME_UNKNOWN_INFORMATION,
- 0xc2, 0x0000, 0x0000, transfer_buffer,
- 0x14, 300);
- if (response < 0) {
- err("%s - error getting first unknown palm command",
__FUNCTION__);
- } else {
- usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14,
transfer_buffer);
+ transfer_buffer = kmalloc (sizeof (*connection_info),
+ GFP_KERNEL);
+ if (!transfer_buffer) {
+ err("%s - kmalloc(%d) failed.", __FUNCTION__,
+ sizeof (*connection_info));
+ return -ENOMEM;
}
+
response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev,
0),
- PALM_GET_SOME_UNKNOWN_INFORMATION,
+ PALM_GET_EXT_CONNECTION_INFORMATION,
0xc2, 0x0000, 0x0000, transfer_buffer,
- 0x14, 300);
+ sizeof (*connection_info), 300);
if (response < 0) {
- err("%s - error getting second unknown palm command",
__FUNCTION__);
+ err("%s - error %d getting connection info",
+ __FUNCTION__, response);
} else {
usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14,
transfer_buffer);
}
}
+ /* Do our horrible Treo hack, if we should */
+ treo_attach(serial);
+
/* ask for the number of bytes available, but ignore the response as it is
broken */
response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
VISOR_REQUEST_BYTES_AVAILABLE,
0xc2, 0x0000, 0x0005, transfer_buffer, 0x02,
300);
@@ -655,6 +752,7 @@
err("%s - error getting bytes available request", __FUNCTION__);
}
+exit:
kfree (transfer_buffer);
/* continue on with initialization */
@@ -702,6 +800,46 @@
return 0;
}
+static void treo_attach (struct usb_serial *serial)
+{
+ struct usb_serial_port *port;
+ int i;
+
+ /* Only do this endpoint hack for the Handspring devices with
+ * interrupt in endpoints, which for now are the Treo devices. */
+ if ((serial->dev->descriptor.idVendor != HANDSPRING_VENDOR_ID) ||
+ (serial->num_interrupt_in == 0))
+ return;
+
+ dbg("%s", __FUNCTION__);
+
+ /* Ok, this is pretty ugly, but these devices want to use the
+ * interrupt endpoint as paired up with a bulk endpoint for a
+ * "virtual serial port". So let's force the endpoints to be
+ * where we want them to be. */
+ for (i = serial->num_bulk_in; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ port->read_urb = serial->port[0].read_urb;
+ port->bulk_in_endpointAddress =
serial->port[0].bulk_in_endpointAddress;
+ port->bulk_in_buffer = serial->port[0].bulk_in_buffer;
+ }
+
+ for (i = serial->num_bulk_out; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ port->write_urb = serial->port[0].write_urb;
+ port->bulk_out_size = serial->port[0].bulk_out_size;
+ port->bulk_out_endpointAddress =
serial->port[0].bulk_out_endpointAddress;
+ port->bulk_out_buffer = serial->port[0].bulk_out_buffer;
+ }
+
+ for (i = serial->num_interrupt_in; i < serial->num_ports; ++i) {
+ port = &serial->port[i];
+ port->interrupt_in_urb = serial->port[0].interrupt_in_urb;
+ port->interrupt_in_endpointAddress =
serial->port[0].interrupt_in_endpointAddress;
+ port->interrupt_in_buffer = serial->port[0].interrupt_in_buffer;
+ }
+}
+
static void visor_shutdown (struct usb_serial *serial)
{
dbg("%s", __FUNCTION__);
@@ -849,4 +987,15 @@
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+
+
+
+
+
+
+
+
+
diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h
--- a/drivers/usb/serial/visor.h Thu Mar 27 16:02:35 2003
+++ b/drivers/usb/serial/visor.h Thu Mar 27 16:02:35 2003
@@ -1,7 +1,7 @@
/*
* USB HandSpring Visor driver
*
- * Copyright (C) 1999 - 2002
+ * Copyright (C) 1999 - 2003
* Greg Kroah-Hartman ([EMAIL PROTECTED])
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#define HANDSPRING_VENDOR_ID 0x082d
#define HANDSPRING_VISOR_ID 0x0100
+#define HANDSPRING_TREO_ID 0x0200
#define PALM_VENDOR_ID 0x0830
#define PALM_M500_ID 0x0001
@@ -40,6 +41,9 @@
#define SONY_CLIE_NX60_ID 0x00DA
#define SONY_CLIE_NZ90V_ID 0x00E9
+#define SAMSUNG_VENDOR_ID 0x04E8
+#define SAMSUNG_SCH_I330_ID 0x8001
+
/****************************************************************************
* Handspring Visor Vendor specific request codes (bRequest values)
* A big thank you to Handspring for providing the following information.
@@ -95,7 +99,36 @@
* PALM_GET_SOME_UNKNOWN_INFORMATION is sent by the host during enumeration to
* get some information from the M series devices, that is currently unknown.
****************************************************************************/
-#define PALM_GET_SOME_UNKNOWN_INFORMATION 0x04
+#define PALM_GET_EXT_CONNECTION_INFORMATION 0x04
+
+/**
+ * struct palm_ext_connection_info - return data from a
PALM_GET_EXT_CONNECTION_INFORMATION request
+ * @num_ports: maximum number of functions/connections in use
+ * @endpoint_numbers_different: will be 1 if in and out endpoints numbers are
+ * different, otherwise it is 0. If value is 1, then
+ * connections.end_point_info is non-zero. If value is 0, then
+ * connections.port contains the endpoint number, which is the same for in
+ * and out.
+ * @port_function_id: contains the creator id of the applicaton that opened
+ * this connection.
+ * @port: contains the in/out endpoint number. Is 0 if in and out endpoint
+ * numbers are different.
+ * @end_point_info: high nubbe is in endpoint and low nibble will indicate out
+ * endpoint. Is 0 if in and out endpoints are the same.
+ *
+ * The maximum number of connections currently supported is 2
+ */
+struct palm_ext_connection_info {
+ __u8 num_ports;
+ __u8 endpoint_numbers_different;
+ __u16 reserved1;
+ struct {
+ __u32 port_function_id;
+ __u8 port;
+ __u8 end_point_info;
+ __u16 reserved;
+ } connections[2];
+};
#endif
-------------------------------------------------------
This SF.net email is sponsored by:
The Definitive IT and Networking Event. Be There!
NetWorld+Interop Las Vegas 2003 -- Register today!
http://ads.sourceforge.net/cgi-bin/redirect.pl?keyn0001en
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel