Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * AIRcable USB Bluetooth Dongle Driver.
0004  *
0005  * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
0006  * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
0007  *
0008  * The device works as an standard CDC device, it has 2 interfaces, the first
0009  * one is for firmware access and the second is the serial one.
0010  * The protocol is very simply, there are two possibilities reading or writing.
0011  * When writing the first urb must have a Header that starts with 0x20 0x29 the
0012  * next two bytes must say how much data will be sent.
0013  * When reading the process is almost equal except that the header starts with
0014  * 0x00 0x20.
0015  *
0016  * The device simply need some stuff to understand data coming from the usb
0017  * buffer: The First and Second byte is used for a Header, the Third and Fourth
0018  * tells the  device the amount of information the package holds.
0019  * Packages are 60 bytes long Header Stuff.
0020  * When writing to the device the first two bytes of the header are 0x20 0x29
0021  * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
0022  * situation, when too much data arrives to the device because it sends the data
0023  * but with out the header. I will use a simply hack to override this situation,
0024  * if there is data coming that does not contain any header, then that is data
0025  * that must go directly to the tty, as there is no documentation about if there
0026  * is any other control code, I will simply check for the first
0027  * one.
0028  *
0029  * I have taken some info from a Greg Kroah-Hartman article:
0030  * http://www.linuxjournal.com/article/6573
0031  * And from Linux Device Driver Kit CD, which is a great work, the authors taken
0032  * the work to recompile lots of information an knowledge in drivers development
0033  * and made it all available inside a cd.
0034  * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
0035  *
0036  */
0037 
0038 #include <asm/unaligned.h>
0039 #include <linux/tty.h>
0040 #include <linux/slab.h>
0041 #include <linux/module.h>
0042 #include <linux/tty_flip.h>
0043 #include <linux/usb.h>
0044 #include <linux/usb/serial.h>
0045 
0046 /* Vendor and Product ID */
0047 #define AIRCABLE_VID        0x16CA
0048 #define AIRCABLE_USB_PID    0x1502
0049 
0050 /* Protocol Stuff */
0051 #define HCI_HEADER_LENGTH   0x4
0052 #define TX_HEADER_0     0x20
0053 #define TX_HEADER_1     0x29
0054 #define RX_HEADER_0     0x00
0055 #define RX_HEADER_1     0x20
0056 #define HCI_COMPLETE_FRAME  64
0057 
0058 /* rx_flags */
0059 #define THROTTLED       0x01
0060 #define ACTUALLY_THROTTLED  0x02
0061 
0062 #define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>, Johan Hovold <jhovold@gmail.com>"
0063 #define DRIVER_DESC "AIRcable USB Driver"
0064 
0065 /* ID table that will be registered with USB core */
0066 static const struct usb_device_id id_table[] = {
0067     { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
0068     { },
0069 };
0070 MODULE_DEVICE_TABLE(usb, id_table);
0071 
0072 static int aircable_prepare_write_buffer(struct usb_serial_port *port,
0073                         void *dest, size_t size)
0074 {
0075     int count;
0076     unsigned char *buf = dest;
0077 
0078     count = kfifo_out_locked(&port->write_fifo, buf + HCI_HEADER_LENGTH,
0079                     size - HCI_HEADER_LENGTH, &port->lock);
0080     buf[0] = TX_HEADER_0;
0081     buf[1] = TX_HEADER_1;
0082     put_unaligned_le16(count, &buf[2]);
0083 
0084     return count + HCI_HEADER_LENGTH;
0085 }
0086 
0087 static int aircable_calc_num_ports(struct usb_serial *serial,
0088                     struct usb_serial_endpoints *epds)
0089 {
0090     /* Ignore the first interface, which has no bulk endpoints. */
0091     if (epds->num_bulk_out == 0) {
0092         dev_dbg(&serial->interface->dev,
0093             "ignoring interface with no bulk-out endpoints\n");
0094         return -ENODEV;
0095     }
0096 
0097     return 1;
0098 }
0099 
0100 static int aircable_process_packet(struct usb_serial_port *port,
0101         int has_headers, char *packet, int len)
0102 {
0103     if (has_headers) {
0104         len -= HCI_HEADER_LENGTH;
0105         packet += HCI_HEADER_LENGTH;
0106     }
0107     if (len <= 0) {
0108         dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
0109         return 0;
0110     }
0111 
0112     tty_insert_flip_string(&port->port, packet, len);
0113 
0114     return len;
0115 }
0116 
0117 static void aircable_process_read_urb(struct urb *urb)
0118 {
0119     struct usb_serial_port *port = urb->context;
0120     char *data = urb->transfer_buffer;
0121     int has_headers;
0122     int count;
0123     int len;
0124     int i;
0125 
0126     has_headers = (urb->actual_length > 2 && data[0] == RX_HEADER_0);
0127 
0128     count = 0;
0129     for (i = 0; i < urb->actual_length; i += HCI_COMPLETE_FRAME) {
0130         len = min_t(int, urb->actual_length - i, HCI_COMPLETE_FRAME);
0131         count += aircable_process_packet(port, has_headers,
0132                                 &data[i], len);
0133     }
0134 
0135     if (count)
0136         tty_flip_buffer_push(&port->port);
0137 }
0138 
0139 static struct usb_serial_driver aircable_device = {
0140     .driver = {
0141         .owner =    THIS_MODULE,
0142         .name =     "aircable",
0143     },
0144     .id_table =         id_table,
0145     .bulk_out_size =    HCI_COMPLETE_FRAME,
0146     .calc_num_ports =   aircable_calc_num_ports,
0147     .process_read_urb = aircable_process_read_urb,
0148     .prepare_write_buffer = aircable_prepare_write_buffer,
0149     .throttle =     usb_serial_generic_throttle,
0150     .unthrottle =       usb_serial_generic_unthrottle,
0151 };
0152 
0153 static struct usb_serial_driver * const serial_drivers[] = {
0154     &aircable_device, NULL
0155 };
0156 
0157 module_usb_serial_driver(serial_drivers, id_table);
0158 
0159 MODULE_AUTHOR(DRIVER_AUTHOR);
0160 MODULE_DESCRIPTION(DRIVER_DESC);
0161 MODULE_LICENSE("GPL v2");