Buffers    Files    Tools    Mule   
Date: 2003-02-01
Author: J. Steingraeber

This is *very* experimental stuff!

This was tested with a SuSE kernel 2.2.14 as it comes with SuSE 6.4
(lx_suse-2.2.14.SuSE-10). I think USB normally is supported with
2.2.18 and above.

One (minor) problem was that the major number of the ttyUSB devices were
incorrectly set to 240, so I had to correct is with

linux:~ # mknod /dev/ttyUSB0 c 188 0
linux:~ # mknod /dev/ttyUSB1 c 188 1
linux:~ # mknod /dev/ttyUSB2 c 188 2
linux:~ # mknod /dev/ttyUSB3 c 188 3

Then I had a look at the visor related things in usb-serial.h and
usb-serial.c.

PV-S1600 can be handled in a very similar way as the visor. I added
pvs1600_serial_open(), pvs1600_serial_close() and pvs1600_startup()
and a pvs1600_device structure to usb-serial.h. The structure was
filled in with the values from generic_device, not the
handspring_device. I only changed the name to "CASIO PV-S1600", vendor
to 0x07cf and the product to 0x6101. I changed the open and close
functions to point to the pvs1600 versions and added the startup
entry.

Here is the diff for usb-serial.h:
-----------------------------8< cut here -----------------------------
--- usb-serial.h.orig	Wed Jan 29 17:25:32 2003
+++ usb-serial.h	Sat Feb  1 16:41:21 2003
@@ -31,6 +31,9 @@
 
 MODULE_PARM(product, "i");
 MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+static __u16	pvs1600_vendor	= 0x07cf;
+static __u16	pvs1600_product	= 0x6101;
 #endif
 
 
@@ -158,6 +161,32 @@
 	read_bulk_callback:	generic_read_bulk_callback,
 	write_bulk_callback:	generic_write_bulk_callback
 };
+
+/* CASIO PV-S1600 works as a generic serial converter with own startup */
+static int  pvs1600_startup		(struct usb_serial *serial);
+static int  pvs1600_serial_open		(struct tty_struct *tty, struct file *filp);
+static void pvs1600_serial_close		(struct tty_struct *tty, struct file *filp);
+static struct usb_serial_device_type pvs1600_device = {
+	name:			"Casio PV-S1600",
+	idVendor:		&pvs1600_vendor,	/* use the user specified vendor id */
+	idProduct:		&pvs1600_product,	/* use the user specified product id */
+	needs_interrupt_in:	DONT_CARE,		/* don't have to have an interrupt in endpoint */
+	needs_bulk_in:		DONT_CARE,		/* don't have to have a bulk in endpoint */
+	needs_bulk_out:		DONT_CARE,		/* don't have to have a bulk out endpoint */
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		NUM_DONT_CARE,
+	num_bulk_out:		NUM_DONT_CARE,
+  	startup:		pvs1600_startup,
+	num_ports:		1,
+  	open:			pvs1600_serial_open,
+  	close:			pvs1600_serial_close,
+	write:			generic_serial_write,
+	write_room:		generic_write_room,
+	chars_in_buffer:	generic_chars_in_buffer,
+	read_bulk_callback:	generic_read_bulk_callback,
+	write_bulk_callback:	generic_write_bulk_callback
+};
+
 #endif
 
 
@@ -374,6 +403,7 @@
 static struct usb_serial_device_type *usb_serial_devices[] = {
 #ifdef CONFIG_USB_SERIAL_GENERIC
 	&generic_device,
+	&pvs1600_device,
 #endif
 #ifdef CONFIG_USB_SERIAL_WHITEHEAT
 	&whiteheat_fake_device,
----------------------------- cut here >8-----------------------------

For usb-serial.c the new routines are mostly just copied from visor
related versions. The main "trick" is to send an usb_control_msg at
startup with type USB_TYPE_VENDOR and value 0x01. I found this from
a Windows snoop of a FTM session ;-)

Here is the diff for usb-serial.c:
-----------------------------8< cut here -----------------------------
--- usb-serial.c.orig	Sat Feb  1 18:42:10 2003
+++ usb-serial.c	Sat Feb  1 18:44:17 2003
@@ -1101,6 +1101,51 @@
 }
 
 
+static int  pvs1600_startup (struct usb_serial *serial)
+{
+	/* send an unknown initial request */
+	usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), 0x01,
+			 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 
+			 0x0000, 0x0000, NULL, 0x00, 300);
+
+	/* continue on with initialization */
+	return (0);
+}
+
+static int  pvs1600_serial_open		(struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+	int port = MINOR(tty->device) - serial->minor;
+
+	dbg("pvs1600_serial_open port %d", port);
+
+	if (serial->active[port]) {
+		dbg ("device already open");
+		return -EINVAL;
+	}
+
+	serial->active[port] = 1;
+
+	/*Start reading from the device*/
+	if (usb_submit_urb(&serial->read_urb[port]))
+		dbg("usb_submit_urb(read bulk) failed");
+
+	return (0);
+}
+
+static void pvs1600_serial_close		(struct tty_struct *tty, struct file *filp)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+	int port = MINOR(tty->device) - serial->minor;
+	
+	dbg("pvs1600_serial_close port %d", port);
+			 
+	/* shutdown our bulk reads and writes */
+	usb_unlink_urb (&serial->write_urb[port]);
+	usb_unlink_urb (&serial->read_urb[port]);
+	serial->active[port] = 0;
+}
+
 
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
 {
----------------------------- cut here >8-----------------------------

For 2.4 kernels things are very similar. I have not yet done it but I
think that the right place for changes is visor.[ch].

How can this be tested?

I have successfully tested things with "minicom -s", changed port to
/dev/ttyUSB0 and set modem initialisation string empty.

You have first to make some program on the PV open the USB port. You
can use MFSTool by PuBo and select the Server function. But beware:
MFSTool will crash if it cannot open the USB port. You will have to
reset your PV is this happens. Of course it will not crash if things
on the Linux side are working correctly ;-)

Minicom may crash, too, if the device vanishes unexpectedly! So the
right order to do things is:

- insmod usbcore, uhci|ohci, usb-serial
- on PV start MFSTool and start the server
- start minicom
- close minicom
- on PV stop the server and exit MFSTool

Have fun!


--:%%  pvs1600_usb.txt  (Text Fill)----ALL------
M-x view-file ../index.html