+/*
+ * cp2101_ctlmsg
+ * A generic usb control message interface.
+ * Returns the actual size of the data read or written within the message, 0
+ * if no data were read or written, or a negative value to indicate an error.
+ */
+static int cp2101_ctlmsg(struct usb_serial_port* port, u8 request,
+ u8 requestype, u16 value, u16 index, void* data, u16 size)
+{
+ struct usb_device *dev = port->serial->dev;
+ u8 *tbuf;
+ int ret;
+
+ if (!(tbuf = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
+ if (requestype & 0x80) {
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+ requestype, value, index, tbuf, size, 300);
+ if (ret > 0 && size)
+ memcpy(data, tbuf, size);
+ } else {
+ if (size)
+ memcpy(tbuf, data, size);
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+ requestype, value, index, tbuf, size, 300);
+ }
+ kfree(tbuf);
+ if (ret < 0 && ret != -EPIPE) {
+ dev_printk(KERN_DEBUG, &dev->dev, "cp2101: control failed cmd rqt %u "
+ "rq %u len %u ret %d\n", requestype, request, size, ret);
+ }
+ return ret;
+}
+
+static int cp2101_reset(struct usb_serial_port *port)
+{
+ dbg("%s", __FUNCTION__);
+
+#if 1
+ /* Is this better than usb_device_reset? It may be. Once a client issues
+ * the reset ioctl, it must disconnect and reconnect, since the USB
+ * connections are torn down. We also ignore the error return, since the
+ * part resets and doesn't send one...
+ */
+ cp2101_ctlmsg(port, 0xff, 0x40, 0x0008, 0x00, 0, 0);
+#else
+ usb_reset_device(port->serial->dev);
+#endif
+ return 0;
+}
+
+static int cp2101_get_partnum(struct usb_serial_port *port)
+{
+ static u8 _partnum = 0;
+ int ret = CP210x_SUCCESS;
+ if (!_partnum) {
+ u8 addr = port->serial->dev->actconfig->interface[0]->cur_altsetting->
+ endpoint[0].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+ if (addr == 0x03 || addr == 0x02) /* The part is a cp2101 */
+ _partnum = 0x01;
+ else if (addr == 0x01) /* Must query part to determine part number */
+ ret = (cp2101_ctlmsg(port, 0xff, 0xc0, 0x370b, 0x00, &_partnum, 1)
+ == 1) ? CP210x_SUCCESS : CP210x_DEVICE_IO_FAILED;
+ else
+ ret = CP210x_DEVICE_IO_FAILED;
+ }
+ dbg("%s - partnum %u err %d", __FUNCTION__, _partnum, ret);
+ return (ret == CP210x_SUCCESS) ? _partnum : 0;
+}
+
+inline int cp2101_setu16(struct usb_serial_port *port, int cmd,
+ unsigned int value)
+{
+ return cp2101_ctlmsg(port, 0xff, 0x40, 3700 | (cmd & 0xff), value, 0, 0);
+}
+
+/* Populates usbstr with: (len) + (0x03) + unicode(str). Each char in str
+ * takes up two bytes in unicode format, so the resulting len(usbstr) is
+ * 2 * len(str) + 2.
+ * Returns the resulting length of the string in usbstr.
+ * This function can accept overlapping usbstr and str as long as the overlap
+ * does not cause data written to usbstr to overwrite data not yet read from
+ * str.
+ */
+static int make_usb_string(char* usbstr, size_t usblen, char* src,
+ size_t srclen)
+{
+ int len = 0;
+
+ if (usbstr && usblen >= 2 && src && *src && srclen) {
+ char* p;
+
+ /* The usb string format uses a single byte to represent length */
+ if (usblen > 256)
+ usblen = 256;
+
+ p = usbstr + 1;
+ *p++ = 0x03;
+ len = 2;
+ while (srclen && len < usblen) {
+ *p++ = *src++;
+ *p++ = 0;
+ len += 2;
+ srclen--;
+ }
+ *usbstr = (char)len;
+ }
+ return len;
+}
+
+/*
+ * cp2101_setstr
+ *
+ * Set a USB string descriptor using proprietary cp210x control messages.
+ * Return the number of characters actually written.
+ */
+inline int cp2101_setstr(struct usb_serial_port *port, int cmd, char *usbstr)
+{
+ unsigned len = usbstr[0];
+ int ret = cp2101_ctlmsg(port, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, usbstr,
+ len);
+ dbg("%s - cmd 0x%02x len %d ret %d", __FUNCTION__, cmd, len, ret);
+ return ret;
+}
+