Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * USB ZyXEL omni.net driver
0004  *
0005  * Copyright (C) 2013,2017 Johan Hovold <johan@kernel.org>
0006  *
0007  * See Documentation/usb/usb-serial.rst for more information on using this
0008  * driver
0009  *
0010  * Please report both successes and troubles to the author at omninet@kroah.com
0011  */
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/errno.h>
0015 #include <linux/slab.h>
0016 #include <linux/tty.h>
0017 #include <linux/tty_driver.h>
0018 #include <linux/tty_flip.h>
0019 #include <linux/module.h>
0020 #include <linux/uaccess.h>
0021 #include <linux/usb.h>
0022 #include <linux/usb/serial.h>
0023 
0024 #define DRIVER_AUTHOR "Alessandro Zummo"
0025 #define DRIVER_DESC "USB ZyXEL omni.net Driver"
0026 
0027 #define ZYXEL_VENDOR_ID     0x0586
0028 #define ZYXEL_OMNINET_ID    0x1000
0029 #define ZYXEL_OMNI_56K_PLUS_ID  0x1500
0030 /* This one seems to be a re-branded ZyXEL device */
0031 #define BT_IGNITIONPRO_ID   0x2000
0032 
0033 /* function prototypes */
0034 static void omninet_process_read_urb(struct urb *urb);
0035 static int omninet_prepare_write_buffer(struct usb_serial_port *port,
0036                 void *buf, size_t count);
0037 static int omninet_calc_num_ports(struct usb_serial *serial,
0038                 struct usb_serial_endpoints *epds);
0039 static int omninet_port_probe(struct usb_serial_port *port);
0040 static void omninet_port_remove(struct usb_serial_port *port);
0041 
0042 static const struct usb_device_id id_table[] = {
0043     { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
0044     { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNI_56K_PLUS_ID) },
0045     { USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
0046     { }                     /* Terminating entry */
0047 };
0048 MODULE_DEVICE_TABLE(usb, id_table);
0049 
0050 static struct usb_serial_driver zyxel_omninet_device = {
0051     .driver = {
0052         .owner =    THIS_MODULE,
0053         .name =     "omninet",
0054     },
0055     .description =      "ZyXEL - omni.net usb",
0056     .id_table =     id_table,
0057     .num_bulk_out =     2,
0058     .calc_num_ports =   omninet_calc_num_ports,
0059     .port_probe =       omninet_port_probe,
0060     .port_remove =      omninet_port_remove,
0061     .process_read_urb = omninet_process_read_urb,
0062     .prepare_write_buffer = omninet_prepare_write_buffer,
0063 };
0064 
0065 static struct usb_serial_driver * const serial_drivers[] = {
0066     &zyxel_omninet_device, NULL
0067 };
0068 
0069 
0070 /*
0071  * The protocol.
0072  *
0073  * The omni.net always exchange 64 bytes of data with the host. The first
0074  * four bytes are the control header.
0075  *
0076  * oh_seq is a sequence number. Don't know if/how it's used.
0077  * oh_len is the length of the data bytes in the packet.
0078  * oh_xxx Bit-mapped, related to handshaking and status info.
0079  *  I normally set it to 0x03 in transmitted frames.
0080  *  7: Active when the TA is in a CONNECTed state.
0081  *  6: unknown
0082  *  5: handshaking, unknown
0083  *  4: handshaking, unknown
0084  *  3: unknown, usually 0
0085  *  2: unknown, usually 0
0086  *  1: handshaking, unknown, usually set to 1 in transmitted frames
0087  *  0: handshaking, unknown, usually set to 1 in transmitted frames
0088  * oh_pad Probably a pad byte.
0089  *
0090  * After the header you will find data bytes if oh_len was greater than zero.
0091  */
0092 struct omninet_header {
0093     __u8    oh_seq;
0094     __u8    oh_len;
0095     __u8    oh_xxx;
0096     __u8    oh_pad;
0097 };
0098 
0099 struct omninet_data {
0100     __u8    od_outseq;  /* Sequence number for bulk_out URBs */
0101 };
0102 
0103 static int omninet_calc_num_ports(struct usb_serial *serial,
0104                     struct usb_serial_endpoints *epds)
0105 {
0106     /* We need only the second bulk-out for our single-port device. */
0107     epds->bulk_out[0] = epds->bulk_out[1];
0108     epds->num_bulk_out = 1;
0109 
0110     return 1;
0111 }
0112 
0113 static int omninet_port_probe(struct usb_serial_port *port)
0114 {
0115     struct omninet_data *od;
0116 
0117     od = kzalloc(sizeof(*od), GFP_KERNEL);
0118     if (!od)
0119         return -ENOMEM;
0120 
0121     usb_set_serial_port_data(port, od);
0122 
0123     return 0;
0124 }
0125 
0126 static void omninet_port_remove(struct usb_serial_port *port)
0127 {
0128     struct omninet_data *od;
0129 
0130     od = usb_get_serial_port_data(port);
0131     kfree(od);
0132 }
0133 
0134 #define OMNINET_HEADERLEN   4
0135 #define OMNINET_BULKOUTSIZE 64
0136 #define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
0137 
0138 static void omninet_process_read_urb(struct urb *urb)
0139 {
0140     struct usb_serial_port *port = urb->context;
0141     const struct omninet_header *hdr = urb->transfer_buffer;
0142     const unsigned char *data;
0143     size_t data_len;
0144 
0145     if (urb->actual_length <= OMNINET_HEADERLEN || !hdr->oh_len)
0146         return;
0147 
0148     data = (char *)urb->transfer_buffer + OMNINET_HEADERLEN;
0149     data_len = min_t(size_t, urb->actual_length - OMNINET_HEADERLEN,
0150                                 hdr->oh_len);
0151     tty_insert_flip_string(&port->port, data, data_len);
0152     tty_flip_buffer_push(&port->port);
0153 }
0154 
0155 static int omninet_prepare_write_buffer(struct usb_serial_port *port,
0156                     void *buf, size_t count)
0157 {
0158     struct omninet_data *od = usb_get_serial_port_data(port);
0159     struct omninet_header *header = buf;
0160 
0161     count = min_t(size_t, count, OMNINET_PAYLOADSIZE);
0162 
0163     count = kfifo_out_locked(&port->write_fifo, buf + OMNINET_HEADERLEN,
0164             count, &port->lock);
0165 
0166     header->oh_seq = od->od_outseq++;
0167     header->oh_len = count;
0168     header->oh_xxx = 0x03;
0169     header->oh_pad = 0x00;
0170 
0171     /* always 64 bytes */
0172     return OMNINET_BULKOUTSIZE;
0173 }
0174 
0175 module_usb_serial_driver(serial_drivers, id_table);
0176 
0177 MODULE_AUTHOR(DRIVER_AUTHOR);
0178 MODULE_DESCRIPTION(DRIVER_DESC);
0179 MODULE_LICENSE("GPL v2");