1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/tty.h>
19#include <linux/tty_driver.h>
20#include <linux/tty_flip.h>
21#include <linux/module.h>
22#include <linux/uaccess.h>
23#include <linux/usb.h>
24#include <linux/usb/serial.h>
25
26#define DRIVER_AUTHOR "Alessandro Zummo"
27#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
28
29#define ZYXEL_VENDOR_ID 0x0586
30#define ZYXEL_OMNINET_ID 0x1000
31
32#define BT_IGNITIONPRO_ID 0x2000
33
34
35static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port);
36static void omninet_process_read_urb(struct urb *urb);
37static void omninet_write_bulk_callback(struct urb *urb);
38static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
39 const unsigned char *buf, int count);
40static int omninet_write_room(struct tty_struct *tty);
41static void omninet_disconnect(struct usb_serial *serial);
42static int omninet_port_probe(struct usb_serial_port *port);
43static int omninet_port_remove(struct usb_serial_port *port);
44
45static const struct usb_device_id id_table[] = {
46 { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
47 { USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
48 { }
49};
50MODULE_DEVICE_TABLE(usb, id_table);
51
52static struct usb_serial_driver zyxel_omninet_device = {
53 .driver = {
54 .owner = THIS_MODULE,
55 .name = "omninet",
56 },
57 .description = "ZyXEL - omni.net lcd plus usb",
58 .id_table = id_table,
59 .num_ports = 1,
60 .port_probe = omninet_port_probe,
61 .port_remove = omninet_port_remove,
62 .open = omninet_open,
63 .write = omninet_write,
64 .write_room = omninet_write_room,
65 .write_bulk_callback = omninet_write_bulk_callback,
66 .process_read_urb = omninet_process_read_urb,
67 .disconnect = omninet_disconnect,
68};
69
70static struct usb_serial_driver * const serial_drivers[] = {
71 &zyxel_omninet_device, NULL
72};
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97struct omninet_header {
98 __u8 oh_seq;
99 __u8 oh_len;
100 __u8 oh_xxx;
101 __u8 oh_pad;
102};
103
104struct omninet_data {
105 __u8 od_outseq;
106};
107
108static int omninet_port_probe(struct usb_serial_port *port)
109{
110 struct omninet_data *od;
111
112 od = kzalloc(sizeof(*od), GFP_KERNEL);
113 if (!od)
114 return -ENOMEM;
115
116 usb_set_serial_port_data(port, od);
117
118 return 0;
119}
120
121static int omninet_port_remove(struct usb_serial_port *port)
122{
123 struct omninet_data *od;
124
125 od = usb_get_serial_port_data(port);
126 kfree(od);
127
128 return 0;
129}
130
131static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
132{
133 struct usb_serial *serial = port->serial;
134 struct usb_serial_port *wport;
135
136 wport = serial->port[1];
137 tty_port_tty_set(&wport->port, tty);
138
139 return usb_serial_generic_open(tty, port);
140}
141
142#define OMNINET_HEADERLEN 4
143#define OMNINET_BULKOUTSIZE 64
144#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
145
146static void omninet_process_read_urb(struct urb *urb)
147{
148 struct usb_serial_port *port = urb->context;
149 const struct omninet_header *hdr = urb->transfer_buffer;
150 const unsigned char *data;
151 size_t data_len;
152
153 if (urb->actual_length <= OMNINET_HEADERLEN || !hdr->oh_len)
154 return;
155
156 data = (char *)urb->transfer_buffer + OMNINET_HEADERLEN;
157 data_len = min_t(size_t, urb->actual_length - OMNINET_HEADERLEN,
158 hdr->oh_len);
159 tty_insert_flip_string(&port->port, data, data_len);
160 tty_flip_buffer_push(&port->port);
161}
162
163static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
164 const unsigned char *buf, int count)
165{
166 struct usb_serial *serial = port->serial;
167 struct usb_serial_port *wport = serial->port[1];
168
169 struct omninet_data *od = usb_get_serial_port_data(port);
170 struct omninet_header *header = (struct omninet_header *)
171 wport->write_urb->transfer_buffer;
172
173 int result;
174
175 if (count == 0) {
176 dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
177 return 0;
178 }
179
180 if (!test_and_clear_bit(0, &port->write_urbs_free)) {
181 dev_dbg(&port->dev, "%s - already writing\n", __func__);
182 return 0;
183 }
184
185 count = (count > OMNINET_PAYLOADSIZE) ? OMNINET_PAYLOADSIZE : count;
186
187 memcpy(wport->write_urb->transfer_buffer + OMNINET_HEADERLEN,
188 buf, count);
189
190 usb_serial_debug_data(&port->dev, __func__, count,
191 wport->write_urb->transfer_buffer);
192
193 header->oh_seq = od->od_outseq++;
194 header->oh_len = count;
195 header->oh_xxx = 0x03;
196 header->oh_pad = 0x00;
197
198
199 wport->write_urb->transfer_buffer_length = OMNINET_BULKOUTSIZE;
200
201 result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
202 if (result) {
203 set_bit(0, &wport->write_urbs_free);
204 dev_err_console(port,
205 "%s - failed submitting write urb, error %d\n",
206 __func__, result);
207 } else
208 result = count;
209
210 return result;
211}
212
213
214static int omninet_write_room(struct tty_struct *tty)
215{
216 struct usb_serial_port *port = tty->driver_data;
217 struct usb_serial *serial = port->serial;
218 struct usb_serial_port *wport = serial->port[1];
219
220 int room = 0;
221
222 if (test_bit(0, &wport->write_urbs_free))
223 room = wport->bulk_out_size - OMNINET_HEADERLEN;
224
225 dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
226
227 return room;
228}
229
230static void omninet_write_bulk_callback(struct urb *urb)
231{
232
233
234 struct usb_serial_port *port = urb->context;
235 int status = urb->status;
236
237 set_bit(0, &port->write_urbs_free);
238 if (status) {
239 dev_dbg(&port->dev, "%s - nonzero write bulk status received: %d\n",
240 __func__, status);
241 return;
242 }
243
244 usb_serial_port_softint(port);
245}
246
247
248static void omninet_disconnect(struct usb_serial *serial)
249{
250 struct usb_serial_port *wport = serial->port[1];
251
252 usb_kill_urb(wport->write_urb);
253}
254
255module_usb_serial_driver(serial_drivers, id_table);
256
257MODULE_AUTHOR(DRIVER_AUTHOR);
258MODULE_DESCRIPTION(DRIVER_DESC);
259MODULE_LICENSE("GPL");
260