15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * USB HandSpring Visor, Palm m50x, and Sony Clie driver
41da177e4SLinus Torvalds * (supports all of the Palm OS USB devices)
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Copyright (C) 1999 - 2004
71da177e4SLinus Torvalds * Greg Kroah-Hartman (greg@kroah.com)
81da177e4SLinus Torvalds *
9*ecefae6dSMauro Carvalho Chehab * See Documentation/usb/usb-serial.rst for more information on using this
10d60d4396SAlan Cox * driver
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds */
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/tty.h>
181da177e4SLinus Torvalds #include <linux/tty_driver.h>
191da177e4SLinus Torvalds #include <linux/tty_flip.h>
201da177e4SLinus Torvalds #include <linux/module.h>
211da177e4SLinus Torvalds #include <linux/moduleparam.h>
221da177e4SLinus Torvalds #include <linux/spinlock.h>
23d60d4396SAlan Cox #include <linux/uaccess.h>
241da177e4SLinus Torvalds #include <linux/usb.h>
25a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
26acb52cb1SMaciej Szmigiero #include <linux/usb/cdc.h>
271da177e4SLinus Torvalds #include "visor.h"
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds /*
301da177e4SLinus Torvalds * Version Information
311da177e4SLinus Torvalds */
321da177e4SLinus Torvalds #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
331da177e4SLinus Torvalds #define DRIVER_DESC "USB HandSpring Visor / Palm OS driver"
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds /* function prototypes for a handspring visor */
36a509a7e4SAlan Cox static int visor_open(struct tty_struct *tty, struct usb_serial_port *port);
37335f8514SAlan Cox static void visor_close(struct usb_serial_port *port);
38d60d4396SAlan Cox static int visor_probe(struct usb_serial *serial,
39d60d4396SAlan Cox const struct usb_device_id *id);
4007814246SJohan Hovold static int visor_calc_num_ports(struct usb_serial *serial,
4107814246SJohan Hovold struct usb_serial_endpoints *epds);
42da2befa6SJohan Hovold static int clie_5_calc_num_ports(struct usb_serial *serial,
43da2befa6SJohan Hovold struct usb_serial_endpoints *epds);
447d12e780SDavid Howells static void visor_read_int_callback(struct urb *urb);
451da177e4SLinus Torvalds static int clie_3_5_startup(struct usb_serial *serial);
46d60d4396SAlan Cox static int palm_os_3_probe(struct usb_serial *serial,
47d60d4396SAlan Cox const struct usb_device_id *id);
48d60d4396SAlan Cox static int palm_os_4_probe(struct usb_serial *serial,
49d60d4396SAlan Cox const struct usb_device_id *id);
501da177e4SLinus Torvalds
515c6b98ddSJohan Hovold static const struct usb_device_id id_table[] = {
521da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
531da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_3_probe },
541da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID),
551da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
561da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID),
571da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
5804d52461SHendrik Schweppe { USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID),
5904d52461SHendrik Schweppe .driver_info = (kernel_ulong_t)&palm_os_4_probe },
601da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID),
611da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
621da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID),
631da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
641da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID),
651da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
661da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID),
671da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
681da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID),
691da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
701da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID),
711da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
721da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID),
731da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
741da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID),
751da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
76ac21e9ffSgregkh@suse.de { USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650),
77ac21e9ffSgregkh@suse.de .driver_info = (kernel_ulong_t)&palm_os_4_probe },
781da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID),
791da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
801da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID),
811da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
821da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID),
831da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
841da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID),
851da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
861da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID),
871da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
881da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID),
891da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
901da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID),
911da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
921da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID),
931da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
94c8ba84a0SMaximilian Attems { USB_DEVICE(ACER_VENDOR_ID, ACER_S10_ID),
95c8ba84a0SMaximilian Attems .driver_info = (kernel_ulong_t)&palm_os_4_probe },
9682ee3aebSJason A. Donenfeld { USB_DEVICE_INTERFACE_CLASS(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID, 0xff),
971da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
981da177e4SLinus Torvalds { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID),
991da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
100115c1ce5SLarry Battraw { USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID),
101115c1ce5SLarry Battraw .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1021da177e4SLinus Torvalds { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID),
1031da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1041da177e4SLinus Torvalds { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID),
1051da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1061da177e4SLinus Torvalds { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID),
1071da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1081da177e4SLinus Torvalds { USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID),
1091da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1101da177e4SLinus Torvalds { } /* Terminating entry */
1111da177e4SLinus Torvalds };
1121da177e4SLinus Torvalds
1135c6b98ddSJohan Hovold static const struct usb_device_id clie_id_5_table[] = {
1141da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID),
1151da177e4SLinus Torvalds .driver_info = (kernel_ulong_t)&palm_os_4_probe },
1161da177e4SLinus Torvalds { } /* Terminating entry */
1171da177e4SLinus Torvalds };
1181da177e4SLinus Torvalds
1195c6b98ddSJohan Hovold static const struct usb_device_id clie_id_3_5_table[] = {
1201da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
1211da177e4SLinus Torvalds { } /* Terminating entry */
1221da177e4SLinus Torvalds };
1231da177e4SLinus Torvalds
1245c6b98ddSJohan Hovold static const struct usb_device_id id_table_combined[] = {
1251da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) },
1261da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) },
1271da177e4SLinus Torvalds { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO600_ID) },
12804d52461SHendrik Schweppe { USB_DEVICE(GSPDA_VENDOR_ID, GSPDA_XPLORE_M68_ID) },
1291da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M500_ID) },
1301da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) },
1311da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) },
1321da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) },
1331da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) },
1341da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) },
1351da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) },
1361da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) },
137ac21e9ffSgregkh@suse.de { USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) },
1381da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) },
1391da177e4SLinus Torvalds { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) },
1401da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) },
1411da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) },
1421da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) },
1431da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) },
1441da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) },
1451da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) },
1461da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) },
1471da177e4SLinus Torvalds { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) },
1481da177e4SLinus Torvalds { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) },
1491da177e4SLinus Torvalds { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SPH_I500_ID) },
150115c1ce5SLarry Battraw { USB_DEVICE(TAPWAVE_VENDOR_ID, TAPWAVE_ZODIAC_ID) },
1511da177e4SLinus Torvalds { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) },
1521da177e4SLinus Torvalds { USB_DEVICE(ACEECA_VENDOR_ID, ACEECA_MEZ1000_ID) },
1531da177e4SLinus Torvalds { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_7135_ID) },
1541da177e4SLinus Torvalds { USB_DEVICE(FOSSIL_VENDOR_ID, FOSSIL_ABACUS_ID) },
1551da177e4SLinus Torvalds { } /* Terminating entry */
1561da177e4SLinus Torvalds };
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds MODULE_DEVICE_TABLE(usb, id_table_combined);
1591da177e4SLinus Torvalds
160d60d4396SAlan Cox /* All of the device info needed for the Handspring Visor,
161d60d4396SAlan Cox and Palm 4.0 devices */
162ea65370dSGreg Kroah-Hartman static struct usb_serial_driver handspring_device = {
16318fcac35SGreg Kroah-Hartman .driver = {
1641da177e4SLinus Torvalds .owner = THIS_MODULE,
165269bda1cSGreg Kroah-Hartman .name = "visor",
16618fcac35SGreg Kroah-Hartman },
167269bda1cSGreg Kroah-Hartman .description = "Handspring Visor / Palm OS",
1681da177e4SLinus Torvalds .id_table = id_table,
1691da177e4SLinus Torvalds .num_ports = 2,
1709a1f298fSJohan Hovold .bulk_out_size = 256,
1711da177e4SLinus Torvalds .open = visor_open,
1721da177e4SLinus Torvalds .close = visor_close,
173214916f2SJohan Hovold .throttle = usb_serial_generic_throttle,
174214916f2SJohan Hovold .unthrottle = usb_serial_generic_unthrottle,
1751da177e4SLinus Torvalds .probe = visor_probe,
1761da177e4SLinus Torvalds .calc_num_ports = visor_calc_num_ports,
1771da177e4SLinus Torvalds .read_int_callback = visor_read_int_callback,
1781da177e4SLinus Torvalds };
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds /* All of the device info needed for the Clie UX50, TH55 Palm 5.0 devices */
181ea65370dSGreg Kroah-Hartman static struct usb_serial_driver clie_5_device = {
18218fcac35SGreg Kroah-Hartman .driver = {
1831da177e4SLinus Torvalds .owner = THIS_MODULE,
184269bda1cSGreg Kroah-Hartman .name = "clie_5",
18518fcac35SGreg Kroah-Hartman },
186269bda1cSGreg Kroah-Hartman .description = "Sony Clie 5.0",
1871da177e4SLinus Torvalds .id_table = clie_id_5_table,
1881da177e4SLinus Torvalds .num_ports = 2,
189da2befa6SJohan Hovold .num_bulk_out = 2,
1909a1f298fSJohan Hovold .bulk_out_size = 256,
1911da177e4SLinus Torvalds .open = visor_open,
1921da177e4SLinus Torvalds .close = visor_close,
193214916f2SJohan Hovold .throttle = usb_serial_generic_throttle,
194214916f2SJohan Hovold .unthrottle = usb_serial_generic_unthrottle,
1951da177e4SLinus Torvalds .probe = visor_probe,
196da2befa6SJohan Hovold .calc_num_ports = clie_5_calc_num_ports,
1971da177e4SLinus Torvalds .read_int_callback = visor_read_int_callback,
1981da177e4SLinus Torvalds };
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds /* device info for the Sony Clie OS version 3.5 */
201ea65370dSGreg Kroah-Hartman static struct usb_serial_driver clie_3_5_device = {
20218fcac35SGreg Kroah-Hartman .driver = {
2031da177e4SLinus Torvalds .owner = THIS_MODULE,
204269bda1cSGreg Kroah-Hartman .name = "clie_3.5",
20518fcac35SGreg Kroah-Hartman },
206269bda1cSGreg Kroah-Hartman .description = "Sony Clie 3.5",
2071da177e4SLinus Torvalds .id_table = clie_id_3_5_table,
2081da177e4SLinus Torvalds .num_ports = 1,
2099a1f298fSJohan Hovold .bulk_out_size = 256,
2101da177e4SLinus Torvalds .open = visor_open,
2111da177e4SLinus Torvalds .close = visor_close,
212214916f2SJohan Hovold .throttle = usb_serial_generic_throttle,
213214916f2SJohan Hovold .unthrottle = usb_serial_generic_unthrottle,
2141da177e4SLinus Torvalds .attach = clie_3_5_startup,
2151da177e4SLinus Torvalds };
2161da177e4SLinus Torvalds
21729618e9fSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
21829618e9fSAlan Stern &handspring_device, &clie_5_device, &clie_3_5_device, NULL
21929618e9fSAlan Stern };
22029618e9fSAlan Stern
2211da177e4SLinus Torvalds /******************************************************************************
2221da177e4SLinus Torvalds * Handspring Visor specific driver functions
2231da177e4SLinus Torvalds ******************************************************************************/
visor_open(struct tty_struct * tty,struct usb_serial_port * port)224a509a7e4SAlan Cox static int visor_open(struct tty_struct *tty, struct usb_serial_port *port)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds int result = 0;
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds if (!port->read_urb) {
2291da177e4SLinus Torvalds /* this is needed for some brain dead Sony devices */
2301da177e4SLinus Torvalds dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n");
2311da177e4SLinus Torvalds return -ENODEV;
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds /* Start reading from the device */
235214916f2SJohan Hovold result = usb_serial_generic_open(tty, port);
236214916f2SJohan Hovold if (result)
2371da177e4SLinus Torvalds goto exit;
2381da177e4SLinus Torvalds
2391da177e4SLinus Torvalds if (port->interrupt_in_urb) {
2404d9b4001SGreg Kroah-Hartman dev_dbg(&port->dev, "adding interrupt input for treo\n");
2411da177e4SLinus Torvalds result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
2421da177e4SLinus Torvalds if (result)
243d60d4396SAlan Cox dev_err(&port->dev,
244d60d4396SAlan Cox "%s - failed submitting interrupt urb, error %d\n",
245441b62c1SHarvey Harrison __func__, result);
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds exit:
2481da177e4SLinus Torvalds return result;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds
visor_close(struct usb_serial_port * port)252335f8514SAlan Cox static void visor_close(struct usb_serial_port *port)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds unsigned char *transfer_buffer;
2551da177e4SLinus Torvalds
256214916f2SJohan Hovold usb_serial_generic_close(port);
2571da177e4SLinus Torvalds usb_kill_urb(port->interrupt_in_urb);
2581da177e4SLinus Torvalds
2591da177e4SLinus Torvalds transfer_buffer = kmalloc(0x12, GFP_KERNEL);
2607620c33aSJohan Hovold if (!transfer_buffer)
2617620c33aSJohan Hovold return;
2621da177e4SLinus Torvalds usb_control_msg(port->serial->dev,
2631da177e4SLinus Torvalds usb_rcvctrlpipe(port->serial->dev, 0),
2641da177e4SLinus Torvalds VISOR_CLOSE_NOTIFICATION, 0xc2,
2651da177e4SLinus Torvalds 0x0000, 0x0000,
2661da177e4SLinus Torvalds transfer_buffer, 0x12, 300);
2671da177e4SLinus Torvalds kfree(transfer_buffer);
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds
visor_read_int_callback(struct urb * urb)2707d12e780SDavid Howells static void visor_read_int_callback(struct urb *urb)
2711da177e4SLinus Torvalds {
272cdc97792SMing Lei struct usb_serial_port *port = urb->context;
27338e8c910SGreg Kroah-Hartman int status = urb->status;
2741da177e4SLinus Torvalds int result;
2751da177e4SLinus Torvalds
27638e8c910SGreg Kroah-Hartman switch (status) {
2771da177e4SLinus Torvalds case 0:
2781da177e4SLinus Torvalds /* success */
2791da177e4SLinus Torvalds break;
2801da177e4SLinus Torvalds case -ECONNRESET:
2811da177e4SLinus Torvalds case -ENOENT:
2821da177e4SLinus Torvalds case -ESHUTDOWN:
2831da177e4SLinus Torvalds /* this urb is terminated, clean up */
2844d9b4001SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
285441b62c1SHarvey Harrison __func__, status);
2861da177e4SLinus Torvalds return;
2871da177e4SLinus Torvalds default:
2884d9b4001SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
289441b62c1SHarvey Harrison __func__, status);
2901da177e4SLinus Torvalds goto exit;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds
2931da177e4SLinus Torvalds /*
2941da177e4SLinus Torvalds * This information is still unknown what it can be used for.
2951da177e4SLinus Torvalds * If anyone has an idea, please let the author know...
2961da177e4SLinus Torvalds *
2971da177e4SLinus Torvalds * Rumor has it this endpoint is used to notify when data
2981da177e4SLinus Torvalds * is ready to be read from the bulk ones.
2991da177e4SLinus Torvalds */
30059d33f2fSGreg Kroah-Hartman usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
30159d33f2fSGreg Kroah-Hartman urb->transfer_buffer);
3021da177e4SLinus Torvalds
3031da177e4SLinus Torvalds exit:
3041da177e4SLinus Torvalds result = usb_submit_urb(urb, GFP_ATOMIC);
3051da177e4SLinus Torvalds if (result)
306d60d4396SAlan Cox dev_err(&urb->dev->dev,
307d60d4396SAlan Cox "%s - Error %d submitting interrupt urb\n",
308441b62c1SHarvey Harrison __func__, result);
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds
palm_os_3_probe(struct usb_serial * serial,const struct usb_device_id * id)311d60d4396SAlan Cox static int palm_os_3_probe(struct usb_serial *serial,
312d60d4396SAlan Cox const struct usb_device_id *id)
3131da177e4SLinus Torvalds {
3141da177e4SLinus Torvalds struct device *dev = &serial->dev->dev;
3151da177e4SLinus Torvalds struct visor_connection_info *connection_info;
3161da177e4SLinus Torvalds unsigned char *transfer_buffer;
3171da177e4SLinus Torvalds char *string;
3181da177e4SLinus Torvalds int retval = 0;
3191da177e4SLinus Torvalds int i;
3201da177e4SLinus Torvalds int num_ports = 0;
3211da177e4SLinus Torvalds
3221da177e4SLinus Torvalds transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
32310c642d0SJohan Hovold if (!transfer_buffer)
3241da177e4SLinus Torvalds return -ENOMEM;
3251da177e4SLinus Torvalds
3261da177e4SLinus Torvalds /* send a get connection info request */
3271da177e4SLinus Torvalds retval = usb_control_msg(serial->dev,
3281da177e4SLinus Torvalds usb_rcvctrlpipe(serial->dev, 0),
3291da177e4SLinus Torvalds VISOR_GET_CONNECTION_INFORMATION,
3301da177e4SLinus Torvalds 0xc2, 0x0000, 0x0000, transfer_buffer,
3311da177e4SLinus Torvalds sizeof(*connection_info), 300);
3321da177e4SLinus Torvalds if (retval < 0) {
3331da177e4SLinus Torvalds dev_err(dev, "%s - error %d getting connection information\n",
334441b62c1SHarvey Harrison __func__, retval);
3351da177e4SLinus Torvalds goto exit;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds
3384842ed5bSGreg Kroah-Hartman if (retval != sizeof(*connection_info)) {
3394842ed5bSGreg Kroah-Hartman dev_err(dev, "Invalid connection information received from device\n");
3404842ed5bSGreg Kroah-Hartman retval = -ENODEV;
3414842ed5bSGreg Kroah-Hartman goto exit;
3424842ed5bSGreg Kroah-Hartman }
3434842ed5bSGreg Kroah-Hartman
3444842ed5bSGreg Kroah-Hartman connection_info = (struct visor_connection_info *)transfer_buffer;
3451da177e4SLinus Torvalds
3461da177e4SLinus Torvalds num_ports = le16_to_cpu(connection_info->num_ports);
3474842ed5bSGreg Kroah-Hartman
3484842ed5bSGreg Kroah-Hartman /* Handle devices that report invalid stuff here. */
3494842ed5bSGreg Kroah-Hartman if (num_ports == 0 || num_ports > 2) {
3504842ed5bSGreg Kroah-Hartman dev_warn(dev, "%s: No valid connect info available\n",
3514842ed5bSGreg Kroah-Hartman serial->type->description);
3524842ed5bSGreg Kroah-Hartman num_ports = 2;
3534842ed5bSGreg Kroah-Hartman }
3544842ed5bSGreg Kroah-Hartman
3551da177e4SLinus Torvalds for (i = 0; i < num_ports; ++i) {
3564842ed5bSGreg Kroah-Hartman switch (connection_info->connections[i].port_function_id) {
3571da177e4SLinus Torvalds case VISOR_FUNCTION_GENERIC:
3581da177e4SLinus Torvalds string = "Generic";
3591da177e4SLinus Torvalds break;
3601da177e4SLinus Torvalds case VISOR_FUNCTION_DEBUGGER:
3611da177e4SLinus Torvalds string = "Debugger";
3621da177e4SLinus Torvalds break;
3631da177e4SLinus Torvalds case VISOR_FUNCTION_HOTSYNC:
3641da177e4SLinus Torvalds string = "HotSync";
3651da177e4SLinus Torvalds break;
3661da177e4SLinus Torvalds case VISOR_FUNCTION_CONSOLE:
3671da177e4SLinus Torvalds string = "Console";
3681da177e4SLinus Torvalds break;
3691da177e4SLinus Torvalds case VISOR_FUNCTION_REMOTE_FILE_SYS:
3701da177e4SLinus Torvalds string = "Remote File System";
3711da177e4SLinus Torvalds break;
3721da177e4SLinus Torvalds default:
3731da177e4SLinus Torvalds string = "unknown";
3741da177e4SLinus Torvalds break;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds dev_info(dev, "%s: port %d, is for %s use\n",
377269bda1cSGreg Kroah-Hartman serial->type->description,
3781da177e4SLinus Torvalds connection_info->connections[i].port, string);
3791da177e4SLinus Torvalds }
380269bda1cSGreg Kroah-Hartman dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
3811da177e4SLinus Torvalds num_ports);
3821da177e4SLinus Torvalds
3831da177e4SLinus Torvalds /*
3841da177e4SLinus Torvalds * save off our num_ports info so that we can use it in the
3851da177e4SLinus Torvalds * calc_num_ports callback
3861da177e4SLinus Torvalds */
3871da177e4SLinus Torvalds usb_set_serial_data(serial, (void *)(long)num_ports);
3881da177e4SLinus Torvalds
389d60d4396SAlan Cox /* ask for the number of bytes available, but ignore the
390d60d4396SAlan Cox response as it is broken */
3911da177e4SLinus Torvalds retval = usb_control_msg(serial->dev,
3921da177e4SLinus Torvalds usb_rcvctrlpipe(serial->dev, 0),
3931da177e4SLinus Torvalds VISOR_REQUEST_BYTES_AVAILABLE,
3941da177e4SLinus Torvalds 0xc2, 0x0000, 0x0005, transfer_buffer,
3951da177e4SLinus Torvalds 0x02, 300);
3961da177e4SLinus Torvalds if (retval < 0)
3971da177e4SLinus Torvalds dev_err(dev, "%s - error %d getting bytes available request\n",
398441b62c1SHarvey Harrison __func__, retval);
3991da177e4SLinus Torvalds retval = 0;
4001da177e4SLinus Torvalds
4011da177e4SLinus Torvalds exit:
4021da177e4SLinus Torvalds kfree(transfer_buffer);
4031da177e4SLinus Torvalds
4041da177e4SLinus Torvalds return retval;
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds
palm_os_4_probe(struct usb_serial * serial,const struct usb_device_id * id)407d60d4396SAlan Cox static int palm_os_4_probe(struct usb_serial *serial,
408d60d4396SAlan Cox const struct usb_device_id *id)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds struct device *dev = &serial->dev->dev;
4111da177e4SLinus Torvalds struct palm_ext_connection_info *connection_info;
4121da177e4SLinus Torvalds unsigned char *transfer_buffer;
4131da177e4SLinus Torvalds int retval;
4141da177e4SLinus Torvalds
4151da177e4SLinus Torvalds transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL);
41610c642d0SJohan Hovold if (!transfer_buffer)
4171da177e4SLinus Torvalds return -ENOMEM;
4181da177e4SLinus Torvalds
4191da177e4SLinus Torvalds retval = usb_control_msg(serial->dev,
4201da177e4SLinus Torvalds usb_rcvctrlpipe(serial->dev, 0),
4211da177e4SLinus Torvalds PALM_GET_EXT_CONNECTION_INFORMATION,
4221da177e4SLinus Torvalds 0xc2, 0x0000, 0x0000, transfer_buffer,
4231da177e4SLinus Torvalds sizeof(*connection_info), 300);
4241da177e4SLinus Torvalds if (retval < 0)
4251da177e4SLinus Torvalds dev_err(dev, "%s - error %d getting connection info\n",
426441b62c1SHarvey Harrison __func__, retval);
4271da177e4SLinus Torvalds else
42859d33f2fSGreg Kroah-Hartman usb_serial_debug_data(dev, __func__, retval, transfer_buffer);
4291da177e4SLinus Torvalds
4301da177e4SLinus Torvalds kfree(transfer_buffer);
4311da177e4SLinus Torvalds return 0;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds
4341da177e4SLinus Torvalds
visor_probe(struct usb_serial * serial,const struct usb_device_id * id)435d60d4396SAlan Cox static int visor_probe(struct usb_serial *serial,
436d60d4396SAlan Cox const struct usb_device_id *id)
4371da177e4SLinus Torvalds {
4381da177e4SLinus Torvalds int retval = 0;
439d60d4396SAlan Cox int (*startup)(struct usb_serial *serial,
440d60d4396SAlan Cox const struct usb_device_id *id);
4411da177e4SLinus Torvalds
442acb52cb1SMaciej Szmigiero /*
443acb52cb1SMaciej Szmigiero * some Samsung Android phones in modem mode have the same ID
444acb52cb1SMaciej Szmigiero * as SPH-I500, but they are ACM devices, so dont bind to them
445acb52cb1SMaciej Szmigiero */
446acb52cb1SMaciej Szmigiero if (id->idVendor == SAMSUNG_VENDOR_ID &&
447acb52cb1SMaciej Szmigiero id->idProduct == SAMSUNG_SPH_I500_ID &&
448acb52cb1SMaciej Szmigiero serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM &&
449acb52cb1SMaciej Szmigiero serial->dev->descriptor.bDeviceSubClass ==
450acb52cb1SMaciej Szmigiero USB_CDC_SUBCLASS_ACM)
451acb52cb1SMaciej Szmigiero return -ENODEV;
452acb52cb1SMaciej Szmigiero
4531da177e4SLinus Torvalds if (serial->dev->actconfig->desc.bConfigurationValue != 1) {
454194343d9SGreg Kroah-Hartman dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",
4551da177e4SLinus Torvalds serial->dev->actconfig->desc.bConfigurationValue);
4561da177e4SLinus Torvalds return -ENODEV;
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds
4591da177e4SLinus Torvalds if (id->driver_info) {
4601da177e4SLinus Torvalds startup = (void *)id->driver_info;
4611da177e4SLinus Torvalds retval = startup(serial, id);
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds
4641da177e4SLinus Torvalds return retval;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds
visor_calc_num_ports(struct usb_serial * serial,struct usb_serial_endpoints * epds)46707814246SJohan Hovold static int visor_calc_num_ports(struct usb_serial *serial,
46807814246SJohan Hovold struct usb_serial_endpoints *epds)
4691da177e4SLinus Torvalds {
470ea3c6ebdSJohan Hovold unsigned int vid = le16_to_cpu(serial->dev->descriptor.idVendor);
4711da177e4SLinus Torvalds int num_ports = (int)(long)(usb_get_serial_data(serial));
4721da177e4SLinus Torvalds
4731da177e4SLinus Torvalds if (num_ports)
4741da177e4SLinus Torvalds usb_set_serial_data(serial, NULL);
4751da177e4SLinus Torvalds
476ea3c6ebdSJohan Hovold /*
477ea3c6ebdSJohan Hovold * Only swap the bulk endpoints for the Handspring devices with
478ea3c6ebdSJohan Hovold * interrupt in endpoints, which for now are the Treo devices.
479ea3c6ebdSJohan Hovold */
480ea3c6ebdSJohan Hovold if (!(vid == HANDSPRING_VENDOR_ID || vid == KYOCERA_VENDOR_ID) ||
481ea3c6ebdSJohan Hovold epds->num_interrupt_in == 0)
482ea3c6ebdSJohan Hovold goto out;
483ea3c6ebdSJohan Hovold
484ea3c6ebdSJohan Hovold if (epds->num_bulk_in < 2 || epds->num_interrupt_in < 2) {
485ea3c6ebdSJohan Hovold dev_err(&serial->interface->dev, "missing endpoints\n");
486ea3c6ebdSJohan Hovold return -ENODEV;
487ea3c6ebdSJohan Hovold }
488ea3c6ebdSJohan Hovold
489ea3c6ebdSJohan Hovold /*
490ea3c6ebdSJohan Hovold * It appears that Treos and Kyoceras want to use the
491ea3c6ebdSJohan Hovold * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint,
492ea3c6ebdSJohan Hovold * so let's swap the 1st and 2nd bulk in and interrupt endpoints.
493ea3c6ebdSJohan Hovold * Note that swapping the bulk out endpoints would break lots of
494ea3c6ebdSJohan Hovold * apps that want to communicate on the second port.
495ea3c6ebdSJohan Hovold */
496ea3c6ebdSJohan Hovold swap(epds->bulk_in[0], epds->bulk_in[1]);
497ea3c6ebdSJohan Hovold swap(epds->interrupt_in[0], epds->interrupt_in[1]);
498ea3c6ebdSJohan Hovold out:
4991da177e4SLinus Torvalds return num_ports;
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds
clie_5_calc_num_ports(struct usb_serial * serial,struct usb_serial_endpoints * epds)502da2befa6SJohan Hovold static int clie_5_calc_num_ports(struct usb_serial *serial,
503da2befa6SJohan Hovold struct usb_serial_endpoints *epds)
504da2befa6SJohan Hovold {
505da2befa6SJohan Hovold /*
506da2befa6SJohan Hovold * TH55 registers 2 ports.
507da2befa6SJohan Hovold * Communication in from the UX50/TH55 uses the first bulk-in
508da2befa6SJohan Hovold * endpoint, while communication out to the UX50/TH55 uses the second
509da2befa6SJohan Hovold * bulk-out endpoint.
510da2befa6SJohan Hovold */
511da2befa6SJohan Hovold
512da2befa6SJohan Hovold /*
513da2befa6SJohan Hovold * FIXME: Should we swap the descriptors instead of using the same
514da2befa6SJohan Hovold * bulk-out endpoint for both ports?
515da2befa6SJohan Hovold */
516da2befa6SJohan Hovold epds->bulk_out[0] = epds->bulk_out[1];
517da2befa6SJohan Hovold
518da2befa6SJohan Hovold return serial->type->num_ports;
519da2befa6SJohan Hovold }
520da2befa6SJohan Hovold
clie_3_5_startup(struct usb_serial * serial)5211da177e4SLinus Torvalds static int clie_3_5_startup(struct usb_serial *serial)
5221da177e4SLinus Torvalds {
5231da177e4SLinus Torvalds struct device *dev = &serial->dev->dev;
5241da177e4SLinus Torvalds int result;
525401711cbSJohan Hovold u8 *data;
5261da177e4SLinus Torvalds
527401711cbSJohan Hovold data = kmalloc(1, GFP_KERNEL);
528401711cbSJohan Hovold if (!data)
529401711cbSJohan Hovold return -ENOMEM;
530401711cbSJohan Hovold
5311da177e4SLinus Torvalds /*
5321da177e4SLinus Torvalds * Note that PEG-300 series devices expect the following two calls.
5331da177e4SLinus Torvalds */
5341da177e4SLinus Torvalds
5351da177e4SLinus Torvalds /* get the config number */
5361da177e4SLinus Torvalds result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
5371da177e4SLinus Torvalds USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
538401711cbSJohan Hovold 0, 0, data, 1, 3000);
5391da177e4SLinus Torvalds if (result < 0) {
540d60d4396SAlan Cox dev_err(dev, "%s: get config number failed: %d\n",
541d60d4396SAlan Cox __func__, result);
542401711cbSJohan Hovold goto out;
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds if (result != 1) {
545d60d4396SAlan Cox dev_err(dev, "%s: get config number bad return length: %d\n",
546d60d4396SAlan Cox __func__, result);
547401711cbSJohan Hovold result = -EIO;
548401711cbSJohan Hovold goto out;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds
5511da177e4SLinus Torvalds /* get the interface number */
5521da177e4SLinus Torvalds result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
5531da177e4SLinus Torvalds USB_REQ_GET_INTERFACE,
5541da177e4SLinus Torvalds USB_DIR_IN | USB_RECIP_INTERFACE,
555401711cbSJohan Hovold 0, 0, data, 1, 3000);
5561da177e4SLinus Torvalds if (result < 0) {
557d60d4396SAlan Cox dev_err(dev, "%s: get interface number failed: %d\n",
558d60d4396SAlan Cox __func__, result);
559401711cbSJohan Hovold goto out;
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds if (result != 1) {
562d60d4396SAlan Cox dev_err(dev,
563d60d4396SAlan Cox "%s: get interface number bad return length: %d\n",
564d60d4396SAlan Cox __func__, result);
565401711cbSJohan Hovold result = -EIO;
566401711cbSJohan Hovold goto out;
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds
569214916f2SJohan Hovold result = 0;
570401711cbSJohan Hovold out:
571401711cbSJohan Hovold kfree(data);
572401711cbSJohan Hovold
573401711cbSJohan Hovold return result;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds
57668e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table_combined);
5771da177e4SLinus Torvalds
5781da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
5791da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
580627cfa89SJohan Hovold MODULE_LICENSE("GPL v2");
581