162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB HandSpring Visor, Palm m50x, and Sony Clie driver 462306a36Sopenharmony_ci * (supports all of the Palm OS USB devices) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1999 - 2004 762306a36Sopenharmony_ci * Greg Kroah-Hartman (greg@kroah.com) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 1062306a36Sopenharmony_ci * driver 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/tty.h> 1862306a36Sopenharmony_ci#include <linux/tty_driver.h> 1962306a36Sopenharmony_ci#include <linux/tty_flip.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/moduleparam.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/usb.h> 2562306a36Sopenharmony_ci#include <linux/usb/serial.h> 2662306a36Sopenharmony_ci#include <linux/usb/cdc.h> 2762306a36Sopenharmony_ci#include "visor.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * Version Information 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" 3362306a36Sopenharmony_ci#define DRIVER_DESC "USB HandSpring Visor / Palm OS driver" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* function prototypes for a handspring visor */ 3662306a36Sopenharmony_cistatic int visor_open(struct tty_struct *tty, struct usb_serial_port *port); 3762306a36Sopenharmony_cistatic void visor_close(struct usb_serial_port *port); 3862306a36Sopenharmony_cistatic int visor_probe(struct usb_serial *serial, 3962306a36Sopenharmony_ci const struct usb_device_id *id); 4062306a36Sopenharmony_cistatic int visor_calc_num_ports(struct usb_serial *serial, 4162306a36Sopenharmony_ci struct usb_serial_endpoints *epds); 4262306a36Sopenharmony_cistatic int clie_5_calc_num_ports(struct usb_serial *serial, 4362306a36Sopenharmony_ci struct usb_serial_endpoints *epds); 4462306a36Sopenharmony_cistatic void visor_read_int_callback(struct urb *urb); 4562306a36Sopenharmony_cistatic int clie_3_5_startup(struct usb_serial *serial); 4662306a36Sopenharmony_cistatic int palm_os_3_probe(struct usb_serial *serial, 4762306a36Sopenharmony_ci const struct usb_device_id *id); 4862306a36Sopenharmony_cistatic int palm_os_4_probe(struct usb_serial *serial, 4962306a36Sopenharmony_ci const struct usb_device_id *id); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 5262306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID), 5362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_3_probe }, 5462306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID), 5562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 5662306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID), 5762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 5862306a36Sopenharmony_ci { USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID), 5962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 6062306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID), 6162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 6262306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID), 6362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 6462306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID), 6562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 6662306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID), 6762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 6862306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID), 6962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 7062306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID), 7162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 7262306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID), 7362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 7462306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID), 7562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 7662306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650), 7762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 7862306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID), 7962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 8062306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID), 8162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 8262306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID), 8362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 8462306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID), 8562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 8662306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID), 8762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 8862306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID), 8962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 9062306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID), 9162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 9262306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID), 9362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 9462306a36Sopenharmony_ci { USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID), 9562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 9662306a36Sopenharmony_ci { USB_DEVICE_INTERFACE_CLASS(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID, 0xff), 9762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 9862306a36Sopenharmony_ci { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID), 9962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 10062306a36Sopenharmony_ci { USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID), 10162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 10262306a36Sopenharmony_ci { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID), 10362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 10462306a36Sopenharmony_ci { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID), 10562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 10662306a36Sopenharmony_ci { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID), 10762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 10862306a36Sopenharmony_ci { USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID), 10962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 11062306a36Sopenharmony_ci { } /* Terminating entry */ 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct usb_device_id clie_id_5_table[] = { 11462306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID), 11562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&palm_os_4_probe }, 11662306a36Sopenharmony_ci { } /* Terminating entry */ 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct usb_device_id clie_id_3_5_table[] = { 12062306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, 12162306a36Sopenharmony_ci { } /* Terminating entry */ 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = { 12562306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, 12662306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) }, 12762306a36Sopenharmony_ci { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) }, 12862306a36Sopenharmony_ci { USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID) }, 12962306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) }, 13062306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, 13162306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, 13262306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) }, 13362306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) }, 13462306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, 13562306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, 13662306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) }, 13762306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) }, 13862306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) }, 13962306a36Sopenharmony_ci { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) }, 14062306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, 14162306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, 14262306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, 14362306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, 14462306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, 14562306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, 14662306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) }, 14762306a36Sopenharmony_ci { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) }, 14862306a36Sopenharmony_ci { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) }, 14962306a36Sopenharmony_ci { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) }, 15062306a36Sopenharmony_ci { USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID) }, 15162306a36Sopenharmony_ci { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) }, 15262306a36Sopenharmony_ci { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) }, 15362306a36Sopenharmony_ci { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) }, 15462306a36Sopenharmony_ci { USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID) }, 15562306a36Sopenharmony_ci { } /* Terminating entry */ 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* All of the device info needed for the Handspring Visor, 16162306a36Sopenharmony_ci and Palm 4.0 devices */ 16262306a36Sopenharmony_cistatic struct usb_serial_driver handspring_device = { 16362306a36Sopenharmony_ci .driver = { 16462306a36Sopenharmony_ci .owner = THIS_MODULE, 16562306a36Sopenharmony_ci .name = "visor", 16662306a36Sopenharmony_ci }, 16762306a36Sopenharmony_ci .description = "Handspring Visor / Palm OS", 16862306a36Sopenharmony_ci .id_table = id_table, 16962306a36Sopenharmony_ci .num_ports = 2, 17062306a36Sopenharmony_ci .bulk_out_size = 256, 17162306a36Sopenharmony_ci .open = visor_open, 17262306a36Sopenharmony_ci .close = visor_close, 17362306a36Sopenharmony_ci .throttle = usb_serial_generic_throttle, 17462306a36Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 17562306a36Sopenharmony_ci .probe = visor_probe, 17662306a36Sopenharmony_ci .calc_num_ports = visor_calc_num_ports, 17762306a36Sopenharmony_ci .read_int_callback = visor_read_int_callback, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */ 18162306a36Sopenharmony_cistatic struct usb_serial_driver clie_5_device = { 18262306a36Sopenharmony_ci .driver = { 18362306a36Sopenharmony_ci .owner = THIS_MODULE, 18462306a36Sopenharmony_ci .name = "clie_5", 18562306a36Sopenharmony_ci }, 18662306a36Sopenharmony_ci .description = "Sony Clie 5.0", 18762306a36Sopenharmony_ci .id_table = clie_id_5_table, 18862306a36Sopenharmony_ci .num_ports = 2, 18962306a36Sopenharmony_ci .num_bulk_out = 2, 19062306a36Sopenharmony_ci .bulk_out_size = 256, 19162306a36Sopenharmony_ci .open = visor_open, 19262306a36Sopenharmony_ci .close = visor_close, 19362306a36Sopenharmony_ci .throttle = usb_serial_generic_throttle, 19462306a36Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 19562306a36Sopenharmony_ci .probe = visor_probe, 19662306a36Sopenharmony_ci .calc_num_ports = clie_5_calc_num_ports, 19762306a36Sopenharmony_ci .read_int_callback = visor_read_int_callback, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* device info for the Sony Clie OS version 3.5 */ 20162306a36Sopenharmony_cistatic struct usb_serial_driver clie_3_5_device = { 20262306a36Sopenharmony_ci .driver = { 20362306a36Sopenharmony_ci .owner = THIS_MODULE, 20462306a36Sopenharmony_ci .name = "clie_3.5", 20562306a36Sopenharmony_ci }, 20662306a36Sopenharmony_ci .description = "Sony Clie 3.5", 20762306a36Sopenharmony_ci .id_table = clie_id_3_5_table, 20862306a36Sopenharmony_ci .num_ports = 1, 20962306a36Sopenharmony_ci .bulk_out_size = 256, 21062306a36Sopenharmony_ci .open = visor_open, 21162306a36Sopenharmony_ci .close = visor_close, 21262306a36Sopenharmony_ci .throttle = usb_serial_generic_throttle, 21362306a36Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 21462306a36Sopenharmony_ci .attach = clie_3_5_startup, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 21862306a36Sopenharmony_ci &handspring_device, &clie_5_device, &clie_3_5_device, NULL 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/****************************************************************************** 22262306a36Sopenharmony_ci * Handspring Visor specific driver functions 22362306a36Sopenharmony_ci ******************************************************************************/ 22462306a36Sopenharmony_cistatic int visor_open(struct tty_struct *tty, struct usb_serial_port *port) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci int result = 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!port->read_urb) { 22962306a36Sopenharmony_ci /* this is needed for some brain dead Sony devices */ 23062306a36Sopenharmony_ci dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n"); 23162306a36Sopenharmony_ci return -ENODEV; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Start reading from the device */ 23562306a36Sopenharmony_ci result = usb_serial_generic_open(tty, port); 23662306a36Sopenharmony_ci if (result) 23762306a36Sopenharmony_ci goto exit; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (port->interrupt_in_urb) { 24062306a36Sopenharmony_ci dev_dbg(&port->dev, "adding interrupt input for treo\n"); 24162306a36Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 24262306a36Sopenharmony_ci if (result) 24362306a36Sopenharmony_ci dev_err(&port->dev, 24462306a36Sopenharmony_ci "%s - failed submitting interrupt urb, error %d\n", 24562306a36Sopenharmony_ci __func__, result); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ciexit: 24862306a36Sopenharmony_ci return result; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void visor_close(struct usb_serial_port *port) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci unsigned char *transfer_buffer; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci usb_serial_generic_close(port); 25762306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci transfer_buffer = kmalloc(0x12, GFP_KERNEL); 26062306a36Sopenharmony_ci if (!transfer_buffer) 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci usb_control_msg(port->serial->dev, 26362306a36Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 26462306a36Sopenharmony_ci VISOR_CLOSE_NOTIFICATION, 0xc2, 26562306a36Sopenharmony_ci 0x0000, 0x0000, 26662306a36Sopenharmony_ci transfer_buffer, 0x12, 300); 26762306a36Sopenharmony_ci kfree(transfer_buffer); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void visor_read_int_callback(struct urb *urb) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 27362306a36Sopenharmony_ci int status = urb->status; 27462306a36Sopenharmony_ci int result; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci switch (status) { 27762306a36Sopenharmony_ci case 0: 27862306a36Sopenharmony_ci /* success */ 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case -ECONNRESET: 28162306a36Sopenharmony_ci case -ENOENT: 28262306a36Sopenharmony_ci case -ESHUTDOWN: 28362306a36Sopenharmony_ci /* this urb is terminated, clean up */ 28462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", 28562306a36Sopenharmony_ci __func__, status); 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", 28962306a36Sopenharmony_ci __func__, status); 29062306a36Sopenharmony_ci goto exit; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * This information is still unknown what it can be used for. 29562306a36Sopenharmony_ci * If anyone has an idea, please let the author know... 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * Rumor has it this endpoint is used to notify when data 29862306a36Sopenharmony_ci * is ready to be read from the bulk ones. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, 30162306a36Sopenharmony_ci urb->transfer_buffer); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciexit: 30462306a36Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 30562306a36Sopenharmony_ci if (result) 30662306a36Sopenharmony_ci dev_err(&urb->dev->dev, 30762306a36Sopenharmony_ci "%s - Error %d submitting interrupt urb\n", 30862306a36Sopenharmony_ci __func__, result); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int palm_os_3_probe(struct usb_serial *serial, 31262306a36Sopenharmony_ci const struct usb_device_id *id) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct device *dev = &serial->dev->dev; 31562306a36Sopenharmony_ci struct visor_connection_info *connection_info; 31662306a36Sopenharmony_ci unsigned char *transfer_buffer; 31762306a36Sopenharmony_ci char *string; 31862306a36Sopenharmony_ci int retval = 0; 31962306a36Sopenharmony_ci int i; 32062306a36Sopenharmony_ci int num_ports = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); 32362306a36Sopenharmony_ci if (!transfer_buffer) 32462306a36Sopenharmony_ci return -ENOMEM; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* send a get connection info request */ 32762306a36Sopenharmony_ci retval = usb_control_msg(serial->dev, 32862306a36Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), 32962306a36Sopenharmony_ci VISOR_GET_CONNECTION_INFORMATION, 33062306a36Sopenharmony_ci 0xc2, 0x0000, 0x0000, transfer_buffer, 33162306a36Sopenharmony_ci sizeof(*connection_info), 300); 33262306a36Sopenharmony_ci if (retval < 0) { 33362306a36Sopenharmony_ci dev_err(dev, "%s - error %d getting connection information\n", 33462306a36Sopenharmony_ci __func__, retval); 33562306a36Sopenharmony_ci goto exit; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (retval != sizeof(*connection_info)) { 33962306a36Sopenharmony_ci dev_err(dev, "Invalid connection information received from device\n"); 34062306a36Sopenharmony_ci retval = -ENODEV; 34162306a36Sopenharmony_ci goto exit; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci connection_info = (struct visor_connection_info *)transfer_buffer; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci num_ports = le16_to_cpu(connection_info->num_ports); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Handle devices that report invalid stuff here. */ 34962306a36Sopenharmony_ci if (num_ports == 0 || num_ports > 2) { 35062306a36Sopenharmony_ci dev_warn(dev, "%s: No valid connect info available\n", 35162306a36Sopenharmony_ci serial->type->description); 35262306a36Sopenharmony_ci num_ports = 2; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci for (i = 0; i < num_ports; ++i) { 35662306a36Sopenharmony_ci switch (connection_info->connections[i].port_function_id) { 35762306a36Sopenharmony_ci case VISOR_FUNCTION_GENERIC: 35862306a36Sopenharmony_ci string = "Generic"; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case VISOR_FUNCTION_DEBUGGER: 36162306a36Sopenharmony_ci string = "Debugger"; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case VISOR_FUNCTION_HOTSYNC: 36462306a36Sopenharmony_ci string = "HotSync"; 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci case VISOR_FUNCTION_CONSOLE: 36762306a36Sopenharmony_ci string = "Console"; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case VISOR_FUNCTION_REMOTE_FILE_SYS: 37062306a36Sopenharmony_ci string = "Remote File System"; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci string = "unknown"; 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci dev_info(dev, "%s: port %d, is for %s use\n", 37762306a36Sopenharmony_ci serial->type->description, 37862306a36Sopenharmony_ci connection_info->connections[i].port, string); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci dev_info(dev, "%s: Number of ports: %d\n", serial->type->description, 38162306a36Sopenharmony_ci num_ports); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * save off our num_ports info so that we can use it in the 38562306a36Sopenharmony_ci * calc_num_ports callback 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci usb_set_serial_data(serial, (void *)(long)num_ports); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* ask for the number of bytes available, but ignore the 39062306a36Sopenharmony_ci response as it is broken */ 39162306a36Sopenharmony_ci retval = usb_control_msg(serial->dev, 39262306a36Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), 39362306a36Sopenharmony_ci VISOR_REQUEST_BYTES_AVAILABLE, 39462306a36Sopenharmony_ci 0xc2, 0x0000, 0x0005, transfer_buffer, 39562306a36Sopenharmony_ci 0x02, 300); 39662306a36Sopenharmony_ci if (retval < 0) 39762306a36Sopenharmony_ci dev_err(dev, "%s - error %d getting bytes available request\n", 39862306a36Sopenharmony_ci __func__, retval); 39962306a36Sopenharmony_ci retval = 0; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ciexit: 40262306a36Sopenharmony_ci kfree(transfer_buffer); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return retval; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int palm_os_4_probe(struct usb_serial *serial, 40862306a36Sopenharmony_ci const struct usb_device_id *id) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct device *dev = &serial->dev->dev; 41162306a36Sopenharmony_ci struct palm_ext_connection_info *connection_info; 41262306a36Sopenharmony_ci unsigned char *transfer_buffer; 41362306a36Sopenharmony_ci int retval; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); 41662306a36Sopenharmony_ci if (!transfer_buffer) 41762306a36Sopenharmony_ci return -ENOMEM; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci retval = usb_control_msg(serial->dev, 42062306a36Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), 42162306a36Sopenharmony_ci PALM_GET_EXT_CONNECTION_INFORMATION, 42262306a36Sopenharmony_ci 0xc2, 0x0000, 0x0000, transfer_buffer, 42362306a36Sopenharmony_ci sizeof(*connection_info), 300); 42462306a36Sopenharmony_ci if (retval < 0) 42562306a36Sopenharmony_ci dev_err(dev, "%s - error %d getting connection info\n", 42662306a36Sopenharmony_ci __func__, retval); 42762306a36Sopenharmony_ci else 42862306a36Sopenharmony_ci usb_serial_debug_data(dev, __func__, retval, transfer_buffer); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci kfree(transfer_buffer); 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int visor_probe(struct usb_serial *serial, 43662306a36Sopenharmony_ci const struct usb_device_id *id) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int retval = 0; 43962306a36Sopenharmony_ci int (*startup)(struct usb_serial *serial, 44062306a36Sopenharmony_ci const struct usb_device_id *id); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * some Samsung Android phones in modem mode have the same ID 44462306a36Sopenharmony_ci * as SPH-I500, but they are ACM devices, so dont bind to them 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci if (id->idVendor == SAMSUNG_VENDOR_ID && 44762306a36Sopenharmony_ci id->idProduct == SAMSUNG_SPH_I500_ID && 44862306a36Sopenharmony_ci serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM && 44962306a36Sopenharmony_ci serial->dev->descriptor.bDeviceSubClass == 45062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ACM) 45162306a36Sopenharmony_ci return -ENODEV; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (serial->dev->actconfig->desc.bConfigurationValue != 1) { 45462306a36Sopenharmony_ci dev_err(&serial->dev->dev, "active config #%d != 1 ??\n", 45562306a36Sopenharmony_ci serial->dev->actconfig->desc.bConfigurationValue); 45662306a36Sopenharmony_ci return -ENODEV; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (id->driver_info) { 46062306a36Sopenharmony_ci startup = (void *)id->driver_info; 46162306a36Sopenharmony_ci retval = startup(serial, id); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return retval; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int visor_calc_num_ports(struct usb_serial *serial, 46862306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci unsigned int vid = le16_to_cpu(serial->dev->descriptor.idVendor); 47162306a36Sopenharmony_ci int num_ports = (int)(long)(usb_get_serial_data(serial)); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (num_ports) 47462306a36Sopenharmony_ci usb_set_serial_data(serial, NULL); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci * Only swap the bulk endpoints for the Handspring devices with 47862306a36Sopenharmony_ci * interrupt in endpoints, which for now are the Treo devices. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (!(vid == HANDSPRING_VENDOR_ID || vid == KYOCERA_VENDOR_ID) || 48162306a36Sopenharmony_ci epds->num_interrupt_in == 0) 48262306a36Sopenharmony_ci goto out; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (epds->num_bulk_in < 2 || epds->num_interrupt_in < 2) { 48562306a36Sopenharmony_ci dev_err(&serial->interface->dev, "missing endpoints\n"); 48662306a36Sopenharmony_ci return -ENODEV; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * It appears that Treos and Kyoceras want to use the 49162306a36Sopenharmony_ci * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint, 49262306a36Sopenharmony_ci * so let's swap the 1st and 2nd bulk in and interrupt endpoints. 49362306a36Sopenharmony_ci * Note that swapping the bulk out endpoints would break lots of 49462306a36Sopenharmony_ci * apps that want to communicate on the second port. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci swap(epds->bulk_in[0], epds->bulk_in[1]); 49762306a36Sopenharmony_ci swap(epds->interrupt_in[0], epds->interrupt_in[1]); 49862306a36Sopenharmony_ciout: 49962306a36Sopenharmony_ci return num_ports; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int clie_5_calc_num_ports(struct usb_serial *serial, 50362306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * TH55 registers 2 ports. 50762306a36Sopenharmony_ci * Communication in from the UX50/TH55 uses the first bulk-in 50862306a36Sopenharmony_ci * endpoint, while communication out to the UX50/TH55 uses the second 50962306a36Sopenharmony_ci * bulk-out endpoint. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * FIXME: Should we swap the descriptors instead of using the same 51462306a36Sopenharmony_ci * bulk-out endpoint for both ports? 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci epds->bulk_out[0] = epds->bulk_out[1]; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return serial->type->num_ports; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int clie_3_5_startup(struct usb_serial *serial) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct device *dev = &serial->dev->dev; 52462306a36Sopenharmony_ci int result; 52562306a36Sopenharmony_ci u8 *data; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci data = kmalloc(1, GFP_KERNEL); 52862306a36Sopenharmony_ci if (!data) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * Note that PEG-300 series devices expect the following two calls. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* get the config number */ 53662306a36Sopenharmony_ci result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 53762306a36Sopenharmony_ci USB_REQ_GET_CONFIGURATION, USB_DIR_IN, 53862306a36Sopenharmony_ci 0, 0, data, 1, 3000); 53962306a36Sopenharmony_ci if (result < 0) { 54062306a36Sopenharmony_ci dev_err(dev, "%s: get config number failed: %d\n", 54162306a36Sopenharmony_ci __func__, result); 54262306a36Sopenharmony_ci goto out; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci if (result != 1) { 54562306a36Sopenharmony_ci dev_err(dev, "%s: get config number bad return length: %d\n", 54662306a36Sopenharmony_ci __func__, result); 54762306a36Sopenharmony_ci result = -EIO; 54862306a36Sopenharmony_ci goto out; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* get the interface number */ 55262306a36Sopenharmony_ci result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 55362306a36Sopenharmony_ci USB_REQ_GET_INTERFACE, 55462306a36Sopenharmony_ci USB_DIR_IN | USB_RECIP_INTERFACE, 55562306a36Sopenharmony_ci 0, 0, data, 1, 3000); 55662306a36Sopenharmony_ci if (result < 0) { 55762306a36Sopenharmony_ci dev_err(dev, "%s: get interface number failed: %d\n", 55862306a36Sopenharmony_ci __func__, result); 55962306a36Sopenharmony_ci goto out; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci if (result != 1) { 56262306a36Sopenharmony_ci dev_err(dev, 56362306a36Sopenharmony_ci "%s: get interface number bad return length: %d\n", 56462306a36Sopenharmony_ci __func__, result); 56562306a36Sopenharmony_ci result = -EIO; 56662306a36Sopenharmony_ci goto out; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci result = 0; 57062306a36Sopenharmony_ciout: 57162306a36Sopenharmony_ci kfree(data); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return result; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 57962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 58062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 581