// SPDX-License-Identifier: GPL-2.0 /* * USB HandSpring Visor, Palm m50x, and Sony Clie driver * (supports all of the Palm OS USB devices) * * Copyright (C) 1999 - 2004 * Greg Kroah-Hartman (greg@kroah.com) * * See Documentation/usb/usb-serial.rst for more information on using this * driver *
*/
/****************************************************************************** * Handspring Visor specific driver functions
******************************************************************************/ staticint visor_open(struct tty_struct *tty, struct usb_serial_port *port)
{ int result = 0;
if (!port->read_urb) { /* this is needed for some brain dead Sony devices */
dev_err(&port->dev, "Device lied about number of ports, please use a lower one.\n"); return -ENODEV;
}
/* Start reading from the device */
result = usb_serial_generic_open(tty, port); if (result) gotoexit;
if (port->interrupt_in_urb) {
dev_dbg(&port->dev, "adding interrupt input for treo\n");
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result)
dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n",
__func__, result);
} exit: return result;
}
staticvoid visor_read_int_callback(struct urb *urb)
{ struct usb_serial_port *port = urb->context; int status = urb->status; int result;
switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */
dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
__func__, status); return; default:
dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
__func__, status); gotoexit;
}
/* * This information is still unknown what it can be used for. * If anyone has an idea, please let the author know... * * Rumor has it this endpoint is used to notify when data * is ready to be read from the bulk ones.
*/
usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
urb->transfer_buffer);
exit:
result = usb_submit_urb(urb, GFP_ATOMIC); if (result)
dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n",
__func__, result);
}
staticint palm_os_3_probe(struct usb_serial *serial, conststruct usb_device_id *id)
{ struct device *dev = &serial->dev->dev; struct visor_connection_info *connection_info; unsignedchar *transfer_buffer; char *string; int retval = 0; int i; int num_ports = 0;
transfer_buffer = kmalloc(sizeof(*connection_info), GFP_KERNEL); if (!transfer_buffer) return -ENOMEM;
/* send a get connection info request */
retval = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
VISOR_GET_CONNECTION_INFORMATION,
0xc2, 0x0000, 0x0000, transfer_buffer, sizeof(*connection_info), 300); if (retval < 0) {
dev_err(dev, "%s - error %d getting connection information\n",
__func__, retval); gotoexit;
}
if (retval != sizeof(*connection_info)) {
dev_err(dev, "Invalid connection information received from device\n");
retval = -ENODEV; gotoexit;
}
/* Handle devices that report invalid stuff here. */ if (num_ports == 0 || num_ports > 2) {
dev_warn(dev, "%s: No valid connect info available\n",
serial->type->description);
num_ports = 2;
}
for (i = 0; i < num_ports; ++i) { switch (connection_info->connections[i].port_function_id) { case VISOR_FUNCTION_GENERIC:
string = "Generic"; break; case VISOR_FUNCTION_DEBUGGER:
string = "Debugger"; break; case VISOR_FUNCTION_HOTSYNC:
string = "HotSync"; break; case VISOR_FUNCTION_CONSOLE:
string = "Console"; break; case VISOR_FUNCTION_REMOTE_FILE_SYS:
string = "Remote File System"; break; default:
string = "unknown"; break;
}
dev_info(dev, "%s: port %d, is for %s use\n",
serial->type->description,
connection_info->connections[i].port, string);
}
dev_info(dev, "%s: Number of ports: %d\n", serial->type->description,
num_ports);
/* * save off our num_ports info so that we can use it in the * calc_num_ports callback
*/
usb_set_serial_data(serial, (void *)(long)num_ports);
/* ask for the number of bytes available, but ignore the
response as it is broken */
retval = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
VISOR_REQUEST_BYTES_AVAILABLE,
0xc2, 0x0000, 0x0005, transfer_buffer,
0x02, 300); if (retval < 0)
dev_err(dev, "%s - error %d getting bytes available request\n",
__func__, retval);
retval = 0;
staticint visor_probe(struct usb_serial *serial, conststruct usb_device_id *id)
{ int retval = 0; int (*startup)(struct usb_serial *serial, conststruct usb_device_id *id);
/* * some Samsung Android phones in modem mode have the same ID * as SPH-I500, but they are ACM devices, so dont bind to them
*/ if (id->idVendor == SAMSUNG_VENDOR_ID &&
id->idProduct == SAMSUNG_SPH_I500_ID &&
serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM &&
serial->dev->descriptor.bDeviceSubClass ==
USB_CDC_SUBCLASS_ACM) return -ENODEV;
staticint visor_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds)
{ unsignedint vid = le16_to_cpu(serial->dev->descriptor.idVendor); int num_ports = (int)(long)(usb_get_serial_data(serial));
if (num_ports)
usb_set_serial_data(serial, NULL);
/* * Only swap the bulk endpoints for the Handspring devices with * interrupt in endpoints, which for now are the Treo devices.
*/ if (!(vid == HANDSPRING_VENDOR_ID || vid == KYOCERA_VENDOR_ID) ||
epds->num_interrupt_in == 0) goto out;
/* * It appears that Treos and Kyoceras want to use the * 1st bulk in endpoint to communicate with the 2nd bulk out endpoint, * so let's swap the 1st and 2nd bulk in and interrupt endpoints. * Note that swapping the bulk out endpoints would break lots of * apps that want to communicate on the second port.
*/
swap(epds->bulk_in[0], epds->bulk_in[1]);
swap(epds->interrupt_in[0], epds->interrupt_in[1]);
out: return num_ports;
}
staticint clie_5_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds)
{ /* * TH55 registers 2 ports. * Communication in from the UX50/TH55 uses the first bulk-in * endpoint, while communication out to the UX50/TH55 uses the second * bulk-out endpoint.
*/
/* * FIXME: Should we swap the descriptors instead of using the same * bulk-out endpoint for both ports?
*/
epds->bulk_out[0] = epds->bulk_out[1];
data = kmalloc(1, GFP_KERNEL); if (!data) return -ENOMEM;
/* * Note that PEG-300 series devices expect the following two calls.
*/
/* get the config number */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
0, 0, data, 1, 3000); if (result < 0) {
dev_err(dev, "%s: get config number failed: %d\n",
__func__, result); goto out;
} if (result != 1) {
dev_err(dev, "%s: get config number bad return length: %d\n",
__func__, result);
result = -EIO; goto out;
}
/* get the interface number */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
USB_REQ_GET_INTERFACE,
USB_DIR_IN | USB_RECIP_INTERFACE,
0, 0, data, 1, 3000); if (result < 0) {
dev_err(dev, "%s: get interface number failed: %d\n",
__func__, result); goto out;
} if (result != 1) {
dev_err(dev, "%s: get interface number bad return length: %d\n",
__func__, result);
result = -EIO; goto out;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.