visor.c revision e33fe4d86f91127f6f7d931ff59ed6cbda06e72b
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB HandSpring Visor, Palm m50x, and Sony Clie driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (supports all of the Palm OS USB devices)
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Copyright (C) 1999 - 2004
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	    Greg Kroah-Hartman (greg@kroah.com)
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
84d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	This program is free software; you can redistribute it and/or
94d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	modify it under the terms of the GNU General Public License version
104d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	2 as published by the Free Software Foundation.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See Documentation/usb/usb-serial.txt for more information on using this driver
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/moduleparam.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
28a969888ce91673c7f4b86520d851a6f0d5a5fa7dGreg Kroah-Hartman#include <linux/usb/serial.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "visor.h"
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB HandSpring Visor / Palm OS driver"
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* function prototypes for a handspring visor */
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_open		(struct usb_serial_port *port, struct file *filp);
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_close		(struct usb_serial_port *port, struct file *filp);
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_write_room		(struct usb_serial_port *port);
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_chars_in_buffer	(struct usb_serial_port *port);
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_throttle	(struct usb_serial_port *port);
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_unthrottle	(struct usb_serial_port *port);
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_probe		(struct usb_serial *serial, const struct usb_device_id *id);
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_calc_num_ports(struct usb_serial *serial);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_shutdown	(struct usb_serial *serial);
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  visor_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
497d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_write_bulk_callback	(struct urb *urb);
507d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_read_bulk_callback	(struct urb *urb);
517d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_read_int_callback	(struct urb *urb);
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  clie_3_5_startup	(struct usb_serial *serial);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  treo_attach		(struct usb_serial *serial);
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int clie_5_attach (struct usb_serial *serial);
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id);
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Parameters that may be passed into the module. */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int debug;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __u16 vendor;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __u16 product;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id id_table [] = {
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_3_probe },
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID),
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID),
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
7004d52461c6ecfc5b72e688b0eb2ead7b555eca25Hendrik Schweppe	{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID),
7104d52461c6ecfc5b72e688b0eb2ead7b555eca25Hendrik Schweppe		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID),
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID),
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID),
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID),
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID),
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID),
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID),
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID),
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
88ac21e9ff08db3d6fac41d356c77fcb531c2e03e1Greg Kroah-Hartman	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650),
89ac21e9ff08db3d6fac41d356c77fcb531c2e03e1Greg Kroah-Hartman		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID),
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID),
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID),
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID),
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID),
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID),
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID),
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
106c8ba84a0c682068a55a5892d6e12e3f196fd792cMaximilian Attems	{ USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID),
107c8ba84a0c682068a55a5892d6e12e3f196fd792cMaximilian Attems		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID),
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID),
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
112115c1ce524869309e4bddcfc3dd112ac76b92defLarry Battraw	{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID),
113115c1ce524869309e4bddcfc3dd112ac76b92defLarry Battraw		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID),
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID),
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID),
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID),
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ },					/* optional parameter entry */
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }					/* Terminating entry */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id clie_id_5_table [] = {
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID),
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.driver_info = (kernel_ulong_t)&palm_os_4_probe },
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ },					/* optional parameter entry */
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }					/* Terminating entry */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id clie_id_3_5_table [] = {
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }					/* Terminating entry */
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id id_table_combined [] = {
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) },
14204d52461c6ecfc5b72e688b0eb2ead7b555eca25Hendrik Schweppe	{ USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID) },
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) },
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) },
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
151ac21e9ff08db3d6fac41d356c77fcb531c2e03e1Greg Kroah-Hartman	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) },
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) },
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) },
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) },
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) },
164115c1ce524869309e4bddcfc3dd112ac76b92defLarry Battraw	{ USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID) },
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) },
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) },
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) },
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID) },
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ },					/* optional parameter entry */
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }					/* Terminating entry */
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, id_table_combined);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver visor_driver = {
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"visor",
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	usb_serial_probe,
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	usb_serial_disconnect,
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	id_table_combined,
180ba9dc657af86d05d2971633e57d1f6f94ed60472Greg Kroah-Hartman	.no_dynamic_id = 	1,
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */
184ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver handspring_device = {
18518fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
18618fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
187269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"visor",
18818fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
189269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"Handspring Visor / Palm OS",
190d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.usb_driver =		&visor_driver,
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		id_table,
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_interrupt_in =	NUM_DONT_CARE,
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_in =		2,
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_out =		2,
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		2,
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			visor_open,
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		visor_close,
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		visor_throttle,
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		visor_unthrottle,
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attach =		treo_attach,
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =		visor_probe,
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.calc_num_ports =	visor_calc_num_ports,
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.shutdown =		visor_shutdown,
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		visor_ioctl,
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		visor_write,
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		visor_write_room,
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	visor_chars_in_buffer,
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	visor_write_bulk_callback,
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	visor_read_bulk_callback,
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_int_callback =	visor_read_int_callback,
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
214ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver clie_5_device = {
21518fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
21618fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
217269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"clie_5",
21818fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
219269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"Sony Clie 5.0",
220d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.usb_driver =		&visor_driver,
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		clie_id_5_table,
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_interrupt_in =	NUM_DONT_CARE,
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_in =		2,
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_out =		2,
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		2,
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			visor_open,
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		visor_close,
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		visor_throttle,
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		visor_unthrottle,
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attach =		clie_5_attach,
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =		visor_probe,
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.calc_num_ports =	visor_calc_num_ports,
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.shutdown =		visor_shutdown,
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		visor_ioctl,
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		visor_write,
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		visor_write_room,
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	visor_chars_in_buffer,
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	visor_write_bulk_callback,
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	visor_read_bulk_callback,
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_int_callback =	visor_read_int_callback,
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* device info for the Sony Clie OS version 3.5 */
244ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver clie_3_5_device = {
24518fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
24618fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
247269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"clie_3.5",
24818fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
249269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"Sony Clie 3.5",
250d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.usb_driver =		&visor_driver,
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		clie_id_3_5_table,
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_interrupt_in =	0,
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_in =		1,
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_out =		1,
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		1,
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			visor_open,
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		visor_close,
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle =		visor_throttle,
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle =		visor_unthrottle,
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attach =		clie_3_5_startup,
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl =		visor_ioctl,
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		visor_write,
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		visor_write_room,
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer =	visor_chars_in_buffer,
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	visor_write_bulk_callback,
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	visor_read_bulk_callback,
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct visor_private {
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t lock;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bytes_in;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bytes_out;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int outstanding_urbs;
274b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	unsigned char throttled;
275b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	unsigned char actually_throttled;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* number of outstanding urbs to prevent userspace DoS from happening */
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define URB_UPPER_LIMIT	42
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int stats;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/******************************************************************************
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Handspring Visor specific driver functions
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ******************************************************************************/
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_open (struct usb_serial_port *port, struct file *filp)
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial *serial = port->serial;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result = 0;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!port->read_urb) {
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this is needed for some brain dead Sony devices */
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n");
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&priv->lock, flags);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->bytes_in = 0;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->bytes_out = 0;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->throttled = 0;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&priv->lock, flags);
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Force low_latency on so that our tty_push actually forces the data
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * through, otherwise it is scheduled, and with high data rates (like
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * with OHCI) data can get lost.
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (port->tty)
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		port->tty->low_latency = 1;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start reading from the device */
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb (port->read_urb, serial->dev,
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   usb_rcvbulkpipe (serial->dev,
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					    port->bulk_in_endpointAddress),
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   port->read_urb->transfer_buffer,
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   port->read_urb->transfer_buffer_length,
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   visor_read_bulk_callback, port);
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result) {
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "%s - failed submitting read urb, error %d\n",
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, result);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (port->interrupt_in_urb) {
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - adding interrupt input for treo", __FUNCTION__);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (result)
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n",
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				__FUNCTION__, result);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_close (struct usb_serial_port *port, struct file * filp)
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *transfer_buffer;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* shutdown our urbs */
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(port->read_urb);
350bcb54a54033ff9359cf64e4283e4f4b92bf9132fMariusz Kozlowski	usb_kill_urb(port->interrupt_in_urb);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
352e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum	mutex_lock(&port->serial->disc_mutex);
353e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum	if (!port->serial->disconnected) {
354e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum		/* Try to send shutdown message, unless the device is gone */
355e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum		transfer_buffer =  kmalloc (0x12, GFP_KERNEL);
356e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum		if (transfer_buffer) {
357e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum			usb_control_msg (port->serial->dev,
358e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum					 usb_rcvctrlpipe(port->serial->dev, 0),
359e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum					 VISOR_CLOSE_NOTIFICATION, 0xc2,
360e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum					 0x0000, 0x0000,
361e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum					 transfer_buffer, 0x12, 300);
362e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum			kfree (transfer_buffer);
363e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum		}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
365e33fe4d86f91127f6f7d931ff59ed6cbda06e72bOliver Neukum	mutex_lock(&port->serial->disc_mutex);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (stats)
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_info(&port->dev, "Bytes In = %d  Bytes Out = %d\n",
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 priv->bytes_in, priv->bytes_out);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_write (struct usb_serial_port *port, const unsigned char *buf, int count)
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial *serial = port->serial;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urb *urb;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *buffer;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&priv->lock, flags);
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore(&priv->lock, flags);
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - write limit hit\n", __FUNCTION__);
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
390b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	priv->outstanding_urbs++;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&priv->lock, flags);
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buffer = kmalloc (count, GFP_ATOMIC);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!buffer) {
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "out of memory\n");
396b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum		count = -ENOMEM;
397b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum		goto error_no_buffer;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	urb = usb_alloc_urb(0, GFP_ATOMIC);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!urb) {
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "no more free urbs\n");
403b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum		count = -ENOMEM;
404b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum		goto error_no_urb;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy (buffer, buf, count);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb (urb, serial->dev,
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   usb_sndbulkpipe (serial->dev,
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					    port->bulk_out_endpointAddress),
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   buffer, count,
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   visor_write_bulk_callback, port);
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send it down the pipe */
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = usb_submit_urb(urb, GFP_ATOMIC);
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status) {
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n",
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, status);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count = status;
423b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum		goto error;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_irqsave(&priv->lock, flags);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->bytes_out += count;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore(&priv->lock, flags);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* we are done with this urb, so let the host driver
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * really free it when it is finished with it */
432b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	usb_free_urb(urb);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
435b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukumerror:
436b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	usb_free_urb(urb);
437b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukumerror_no_urb:
438b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	kfree(buffer);
439b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukumerror_no_buffer:
440b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	spin_lock_irqsave(&priv->lock, flags);
441b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	--priv->outstanding_urbs;
442b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	spin_unlock_irqrestore(&priv->lock, flags);
443b80349b17c6e1236a24616f71e59ed31279de25aOliver Neukum	return count;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_write_room (struct usb_serial_port *port)
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
449bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	struct visor_private *priv = usb_get_serial_port_data(port);
450bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	unsigned long flags;
451bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We really can take anything the user throws at us
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * but let's pick a nice big number to tell the tty
457bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	 * layer that we have lots of free space, unless we don't.
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
459bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev
460bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	spin_lock_irqsave(&priv->lock, flags);
461bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
462bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		spin_unlock_irqrestore(&priv->lock, flags);
463bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		dbg("%s - write limit hit\n", __FUNCTION__);
464bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		return 0;
465bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	}
466bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	spin_unlock_irqrestore(&priv->lock, flags);
467bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 2048;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_chars_in_buffer (struct usb_serial_port *port)
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We can't really account for how much data we
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * have sent out, but hasn't made it through to the
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * device, so just tell the tty layer that everything
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * is flushed.
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4867d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_write_bulk_callback (struct urb *urb)
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
49038e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	int status = urb->status;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* free up the transfer buffer, as usb_free_urb() does not do this */
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (urb->transfer_buffer);
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
49738e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman
49838e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	if (status)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero write bulk status received: %d",
50038e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman		    __FUNCTION__, status);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&priv->lock, flags);
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	--priv->outstanding_urbs;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&priv->lock, flags);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
506cf2c7481d2ff7f0c266de873b2fe93883e9782f9Pete Zaitcev	usb_serial_port_softint(port);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5107d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_read_bulk_callback (struct urb *urb)
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *data = urb->transfer_buffer;
51538e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	int status = urb->status;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tty_struct *tty;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
518b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	int available_room;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
52238e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	if (status) {
52338e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman		dbg("%s - nonzero read bulk status received: %d",
52438e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman		    __FUNCTION__, status);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty = port->tty;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty && urb->actual_length) {
532b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		available_room = tty_buffer_request_room(tty, urb->actual_length);
533b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		if (available_room) {
534b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum			tty_insert_flip_string(tty, data, available_room);
535b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum			tty_flip_buffer_push(tty);
536b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		}
537b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		spin_lock(&priv->lock);
538b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		priv->bytes_in += available_room;
539b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum
540b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	} else {
541b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		spin_lock(&priv->lock);
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Continue trying to always read if we should */
545b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	if (!priv->throttled) {
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_fill_bulk_urb (port->read_urb, port->serial->dev,
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   usb_rcvbulkpipe(port->serial->dev,
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						   port->bulk_in_endpointAddress),
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   port->read_urb->transfer_buffer,
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   port->read_urb->transfer_buffer_length,
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   visor_read_bulk_callback, port);
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (result)
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
555b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	} else {
556b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum		priv->actually_throttled = 1;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
558b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	spin_unlock(&priv->lock);
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5617d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void visor_read_int_callback (struct urb *urb)
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
56438e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	int status = urb->status;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
56738e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman	switch (status) {
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* success */
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ECONNRESET:
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ENOENT:
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case -ESHUTDOWN:
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* this urb is terminated, clean up */
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - urb shutting down with status: %d",
57638e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman		    __FUNCTION__, status);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero urb status received: %d",
58038e8c910ff7a1aafe2923f085df0f74a60f9de3cGreg Kroah-Hartman		    __FUNCTION__, status);
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * This information is still unknown what it can be used for.
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If anyone has an idea, please let the author know...
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Rumor has it this endpoint is used to notify when data
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * is ready to be read from the bulk ones.
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      urb->actual_length, urb->transfer_buffer);
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb (urb, GFP_ATOMIC);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n",
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, result);
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_throttle (struct usb_serial_port *port)
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&priv->lock, flags);
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->throttled = 1;
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&priv->lock, flags);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_unthrottle (struct usb_serial_port *port)
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv = usb_get_serial_port_data(port);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&priv->lock, flags);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->throttled = 0;
622b308e74d9c708ee2a9af14fbe235e0a41216f4edOliver Neukum	priv->actually_throttled = 0;
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&priv->lock, flags);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->read_urb->dev = port->serial->dev;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int palm_os_3_probe (struct usb_serial *serial, const struct usb_device_id *id)
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device *dev = &serial->dev->dev;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_connection_info *connection_info;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *transfer_buffer;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *string;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = 0;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num_ports = 0;
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	transfer_buffer = kmalloc (sizeof (*connection_info), GFP_KERNEL);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!transfer_buffer) {
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __FUNCTION__,
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(*connection_info));
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send a get connection info request */
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_control_msg (serial->dev,
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  usb_rcvctrlpipe(serial->dev, 0),
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  VISOR_GET_CONNECTION_INFORMATION,
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0xc2, 0x0000, 0x0000, transfer_buffer,
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  sizeof(*connection_info), 300);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval < 0) {
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s - error %d getting connection information\n",
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, retval);
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval == sizeof(*connection_info)) {
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	        connection_info = (struct visor_connection_info *)transfer_buffer;
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num_ports = le16_to_cpu(connection_info->num_ports);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < num_ports; ++i) {
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (connection_info->connections[i].port_function_id) {
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case VISOR_FUNCTION_GENERIC:
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "Generic";
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case VISOR_FUNCTION_DEBUGGER:
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "Debugger";
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case VISOR_FUNCTION_HOTSYNC:
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "HotSync";
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case VISOR_FUNCTION_CONSOLE:
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "Console";
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case VISOR_FUNCTION_REMOTE_FILE_SYS:
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "Remote File System";
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				default:
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					string = "unknown";
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_info(dev, "%s: port %d, is for %s use\n",
688269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman				serial->type->description,
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				connection_info->connections[i].port, string);
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* Handle devices that report invalid stuff here.
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (num_ports == 0 || num_ports > 2) {
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_warn (dev, "%s: No valid connect info available\n",
697269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman			serial->type->description);
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num_ports = 2;
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num_ports);
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * save off our num_ports info so that we can use it in the
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * calc_num_ports callback
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_set_serial_data(serial, (void *)(long)num_ports);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* ask for the number of bytes available, but ignore the response as it is broken */
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_control_msg (serial->dev,
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  usb_rcvctrlpipe(serial->dev, 0),
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  VISOR_REQUEST_BYTES_AVAILABLE,
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0xc2, 0x0000, 0x0005, transfer_buffer,
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0x02, 300);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval < 0)
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s - error %d getting bytes available request\n",
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, retval);
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = 0;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (transfer_buffer);
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int palm_os_4_probe (struct usb_serial *serial, const struct usb_device_id *id)
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device *dev = &serial->dev->dev;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct palm_ext_connection_info *connection_info;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char *transfer_buffer;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	transfer_buffer =  kmalloc (sizeof (*connection_info), GFP_KERNEL);
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!transfer_buffer) {
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s - kmalloc(%Zd) failed.\n", __FUNCTION__,
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(*connection_info));
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_control_msg (serial->dev,
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  usb_rcvctrlpipe(serial->dev, 0),
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  PALM_GET_EXT_CONNECTION_INFORMATION,
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0xc2, 0x0000, 0x0000, transfer_buffer,
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  sizeof (*connection_info), 300);
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval < 0)
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s - error %d getting connection info\n",
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			__FUNCTION__, retval);
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_serial_debug_data(debug, &serial->dev->dev, __FUNCTION__,
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      retval, transfer_buffer);
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (transfer_buffer);
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_probe (struct usb_serial *serial, const struct usb_device_id *id)
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval = 0;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int (*startup) (struct usb_serial *serial, const struct usb_device_id *id);
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("active config #%d != 1 ??",
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			serial->dev->actconfig->desc.bConfigurationValue);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (id->driver_info) {
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		startup = (void *)id->driver_info;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = startup(serial, id);
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_calc_num_ports (struct usb_serial *serial)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int num_ports = (int)(long)(usb_get_serial_data(serial));
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (num_ports)
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_set_serial_data(serial, NULL);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num_ports;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int generic_startup(struct usb_serial *serial)
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
793bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	struct usb_serial_port **ports = serial->port;
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct visor_private *priv;
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < serial->num_ports; ++i) {
79880b6ca48321974a6566a1c9048ba34f60420bca6Eric Sesterhenn		priv = kzalloc (sizeof(*priv), GFP_KERNEL);
799bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		if (!priv) {
800bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev			while (i-- != 0) {
801bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev				priv = usb_get_serial_port_data(ports[i]);
802bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev				usb_set_serial_port_data(ports[i], NULL);
803bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev				kfree(priv);
804bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev			}
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
806bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		}
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_init(&priv->lock);
808bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		usb_set_serial_port_data(ports[i], priv);
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int clie_3_5_startup (struct usb_serial *serial)
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device *dev = &serial->dev->dev;
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 data;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Note that PEG-300 series devices expect the following two calls.
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* get the config number */
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0, 0, &data, 1, 3000);
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result < 0) {
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s: get config number failed: %d\n", __FUNCTION__, result);
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return result;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result != 1) {
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s: get config number bad return length: %d\n", __FUNCTION__, result);
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* get the interface number */
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  USB_REQ_GET_INTERFACE,
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  USB_DIR_IN | USB_RECIP_INTERFACE,
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  0, 0, &data, 1, 3000);
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result < 0) {
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s: get interface number failed: %d\n", __FUNCTION__, result);
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return result;
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result != 1) {
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err(dev, "%s: get interface number bad return length: %d\n", __FUNCTION__, result);
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return generic_startup(serial);
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int treo_attach (struct usb_serial *serial)
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *swap_port;
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Only do this endpoint hack for the Handspring devices with
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * interrupt in endpoints, which for now are the Treo devices. */
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!((le16_to_cpu(serial->dev->descriptor.idVendor) == HANDSPRING_VENDOR_ID) ||
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      (le16_to_cpu(serial->dev->descriptor.idVendor) == KYOCERA_VENDOR_ID)) ||
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (serial->num_interrupt_in == 0))
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto generic_startup;
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* It appears that Treos and Kyoceras want to use the
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* 1st bulk in endpoint to communicate with the 2nd bulk out endpoint,
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* so let's swap the 1st and 2nd bulk in and interrupt endpoints.
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* Note that swapping the bulk out endpoints would break lots of
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	* apps that want to communicate on the second port.
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define COPY_PORT(dest, src)						\
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->read_urb = src->read_urb;					\
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->bulk_in_endpointAddress = src->bulk_in_endpointAddress;	\
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->bulk_in_buffer = src->bulk_in_buffer;			\
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->interrupt_in_urb = src->interrupt_in_urb;			\
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->interrupt_in_endpointAddress = src->interrupt_in_endpointAddress;	\
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dest->interrupt_in_buffer = src->interrupt_in_buffer;
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	swap_port = kmalloc(sizeof(*swap_port), GFP_KERNEL);
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!swap_port)
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	COPY_PORT(swap_port, serial->port[0]);
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	COPY_PORT(serial->port[0], serial->port[1]);
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	COPY_PORT(serial->port[1], swap_port);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(swap_port);
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsgeneric_startup:
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return generic_startup(serial);
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int clie_5_attach (struct usb_serial *serial)
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* TH55 registers 2 ports.
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Communication in from the UX50/TH55 uses bulk_in_endpointAddress from port 0
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Communication out to the UX50/TH55 uses bulk_out_endpointAddress from port 1
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Lets do a quick and dirty mapping
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* some sanity check */
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (serial->num_ports < 2)
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* port 0 now uses the modified endpoint Address */
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serial->port[0]->bulk_out_endpointAddress = serial->port[1]->bulk_out_endpointAddress;
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return generic_startup(serial);
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void visor_shutdown (struct usb_serial *serial)
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
918bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	struct visor_private *priv;
919bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	int i;
920bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s", __FUNCTION__);
922bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev
923bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	for (i = 0; i < serial->num_ports; i++) {
924bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		priv = usb_get_serial_port_data(serial->port[i]);
925bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		if (priv) {
926bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev			usb_set_serial_port_data(serial->port[i], NULL);
927bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev			kfree(priv);
928bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev		}
929bd97c4f035b47c1a3ae5cc5ceccdda028b25e9d5Pete Zaitcev	}
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOIOCTLCMD;
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init visor_init (void)
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, retval;
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Only if parameters were passed to us */
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((vendor>0) && (product>0)) {
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct usb_device_id usb_dev_temp[]=
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			{{USB_DEVICE(vendor, product),
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			.driver_info = (kernel_ulong_t)&palm_os_4_probe }};
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Find the last entry in id_table */
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i=0; ; i++) {
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (id_table[i].idVendor==0) {
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				id_table[i] = usb_dev_temp[0];
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Find the last entry in id_table_combined */
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i=0; ; i++) {
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (id_table_combined[i].idVendor==0) {
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				id_table_combined[i] = usb_dev_temp[0];
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info("Untested USB device specified at time of module insertion");
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info("Warning: This is not guaranteed to work");
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info("Using a newer kernel is preferred to this method");
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info("Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x",
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vendor, product);
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&handspring_device);
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_handspring_register;
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&clie_3_5_device);
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_clie_3_5_register;
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&clie_5_device);
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_clie_5_register;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&visor_driver);
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_register;
98017a882fc0c91477b2582a6dfd4ca93ae7eb58cd3Greg Kroah-Hartman	info(DRIVER_DESC);
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_register:
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&clie_5_device);
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_clie_5_register:
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&clie_3_5_device);
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_clie_3_5_register:
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&handspring_device);
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_handspring_register:
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit visor_exit (void)
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister (&visor_driver);
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister (&handspring_device);
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister (&clie_3_5_device);
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister (&clie_5_device);
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(visor_init);
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(visor_exit);
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(stats, bool, S_IRUGO | S_IWUSR);
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(stats, "Enables statistics or not");
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(vendor, ushort, 0);
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(vendor, "User specified vendor ID");
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(product, ushort, 0);
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(product, "User specified product ID");
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1020