]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/usb/serial/upd78f0730.c
USB: serial: add uPD78F0730 USB to Serial Adaptor Driver
[mirror_ubuntu-artful-kernel.git] / drivers / usb / serial / upd78f0730.c
CommitLineData
ea534e0b
MS
1/*
2 * Renesas Electronics uPD78F0730 USB to serial converter driver
3 *
4 * Copyright (C) 2014,2016 Maksim Salau <maksim.salau@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * Protocol of the adaptor is described in the application note U19660EJ1V0AN00
11 * μPD78F0730 8-bit Single-Chip Microcontroller
12 * USB-to-Serial Conversion Software
13 * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf>
14 *
15 * The adaptor functionality is limited to the following:
16 * - data bits: 7 or 8
17 * - stop bits: 1 or 2
18 * - parity: even, odd or none
19 * - flow control: none
20 * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200
21 * - signals: DTR, RTS and BREAK
22 */
23
24#include <linux/module.h>
25#include <linux/slab.h>
26#include <linux/tty.h>
27#include <linux/usb.h>
28#include <linux/usb/serial.h>
29
30#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver"
31
32#define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>"
33
34static const struct usb_device_id id_table[] = {
35 { USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */
36 { USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */
37 {}
38};
39
40MODULE_DEVICE_TABLE(usb, id_table);
41
42/*
43 * Each adaptor is associated with a private structure, that holds the current
44 * state of control signals (DTR, RTS and BREAK).
45 */
46struct upd78f0730_port_private {
47 struct mutex lock; /* mutex to protect line_signals */
48 u8 line_signals;
49};
50
51/* Op-codes of control commands */
52#define UPD78F0730_CMD_LINE_CONTROL 0x00
53#define UPD78F0730_CMD_SET_DTR_RTS 0x01
54#define UPD78F0730_CMD_SET_XON_XOFF_CHR 0x02
55#define UPD78F0730_CMD_OPEN_CLOSE 0x03
56#define UPD78F0730_CMD_SET_ERR_CHR 0x04
57
58/* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */
59#define UPD78F0730_DATA_SIZE_7_BITS 0x00
60#define UPD78F0730_DATA_SIZE_8_BITS 0x01
61#define UPD78F0730_DATA_SIZE_MASK 0x01
62
63/* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */
64#define UPD78F0730_STOP_BIT_1_BIT 0x00
65#define UPD78F0730_STOP_BIT_2_BIT 0x02
66#define UPD78F0730_STOP_BIT_MASK 0x02
67
68/* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */
69#define UPD78F0730_PARITY_NONE 0x00
70#define UPD78F0730_PARITY_EVEN 0x04
71#define UPD78F0730_PARITY_ODD 0x08
72#define UPD78F0730_PARITY_MASK 0x0C
73
74/* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */
75#define UPD78F0730_FLOW_CONTROL_NONE 0x00
76#define UPD78F0730_FLOW_CONTROL_HW 0x10
77#define UPD78F0730_FLOW_CONTROL_SW 0x20
78#define UPD78F0730_FLOW_CONTROL_MASK 0x30
79
80/* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */
81#define UPD78F0730_RTS 0x01
82#define UPD78F0730_DTR 0x02
83#define UPD78F0730_BREAK 0x04
84
85/* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */
86#define UPD78F0730_PORT_CLOSE 0x00
87#define UPD78F0730_PORT_OPEN 0x01
88
89/* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */
90#define UPD78F0730_ERR_CHR_DISABLED 0x00
91#define UPD78F0730_ERR_CHR_ENABLED 0x01
92
93/*
94 * Declaration of command structures
95 */
96
97/* UPD78F0730_CMD_LINE_CONTROL command */
98struct upd78f0730_line_control {
99 u8 opcode;
100 __le32 baud_rate;
101 u8 params;
102} __packed;
103
104/* UPD78F0730_CMD_SET_DTR_RTS command */
105struct upd78f0730_set_dtr_rts {
106 u8 opcode;
107 u8 params;
108};
109
110/* UPD78F0730_CMD_SET_XON_OFF_CHR command */
111struct upd78f0730_set_xon_xoff_chr {
112 u8 opcode;
113 u8 xon;
114 u8 xoff;
115};
116
117/* UPD78F0730_CMD_OPEN_CLOSE command */
118struct upd78f0730_open_close {
119 u8 opcode;
120 u8 state;
121};
122
123/* UPD78F0730_CMD_SET_ERR_CHR command */
124struct upd78f0730_set_err_chr {
125 u8 opcode;
126 u8 state;
127 u8 err_char;
128};
129
130static int upd78f0730_send_ctl(struct usb_serial_port *port,
131 const void *data, int size)
132{
133 struct usb_device *usbdev = port->serial->dev;
134 void *buf;
135 int res;
136
137 if (size <= 0 || !data)
138 return -EINVAL;
139
140 buf = kmemdup(data, size, GFP_KERNEL);
141 if (!buf)
142 return -ENOMEM;
143
144 res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00,
145 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
146 0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT);
147
148 kfree(buf);
149
150 if (res != size) {
151 struct device *dev = &port->dev;
152
153 dev_err(dev, "failed to send control request %02x: %d\n",
154 *(u8 *)data, res);
155 /* The maximum expected length of a transfer is 6 bytes */
156 if (res >= 0)
157 res = -EIO;
158
159 return res;
160 }
161
162 return 0;
163}
164
165static int upd78f0730_port_probe(struct usb_serial_port *port)
166{
167 struct upd78f0730_port_private *private;
168
169 private = kzalloc(sizeof(*private), GFP_KERNEL);
170 if (!private)
171 return -ENOMEM;
172
173 mutex_init(&private->lock);
174 usb_set_serial_port_data(port, private);
175
176 return 0;
177}
178
179static int upd78f0730_port_remove(struct usb_serial_port *port)
180{
181 struct upd78f0730_port_private *private;
182
183 private = usb_get_serial_port_data(port);
184 mutex_destroy(&private->lock);
185 kfree(private);
186
187 return 0;
188}
189
190static int upd78f0730_tiocmget(struct tty_struct *tty)
191{
192 struct device *dev = tty->dev;
193 struct upd78f0730_port_private *private;
194 struct usb_serial_port *port = tty->driver_data;
195 int signals;
196 int res;
197
198 private = usb_get_serial_port_data(port);
199
200 mutex_lock(&private->lock);
201 signals = private->line_signals;
202 mutex_unlock(&private->lock);
203
204 res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) |
205 ((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0);
206
207 dev_dbg(dev, "%s - res = %x\n", __func__, res);
208
209 return res;
210}
211
212static int upd78f0730_tiocmset(struct tty_struct *tty,
213 unsigned int set, unsigned int clear)
214{
215 struct device *dev = tty->dev;
216 struct usb_serial_port *port = tty->driver_data;
217 struct upd78f0730_port_private *private;
218 struct upd78f0730_set_dtr_rts request;
219 int res;
220
221 private = usb_get_serial_port_data(port);
222
223 mutex_lock(&private->lock);
224 if (set & TIOCM_DTR) {
225 private->line_signals |= UPD78F0730_DTR;
226 dev_dbg(dev, "%s - set DTR\n", __func__);
227 }
228 if (set & TIOCM_RTS) {
229 private->line_signals |= UPD78F0730_RTS;
230 dev_dbg(dev, "%s - set RTS\n", __func__);
231 }
232 if (clear & TIOCM_DTR) {
233 private->line_signals &= ~UPD78F0730_DTR;
234 dev_dbg(dev, "%s - clear DTR\n", __func__);
235 }
236 if (clear & TIOCM_RTS) {
237 private->line_signals &= ~UPD78F0730_RTS;
238 dev_dbg(dev, "%s - clear RTS\n", __func__);
239 }
240 request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
241 request.params = private->line_signals;
242
243 res = upd78f0730_send_ctl(port, &request, sizeof(request));
244 mutex_unlock(&private->lock);
245
246 return res;
247}
248
249static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state)
250{
251 struct device *dev = tty->dev;
252 struct upd78f0730_port_private *private;
253 struct usb_serial_port *port = tty->driver_data;
254 struct upd78f0730_set_dtr_rts request;
255
256 private = usb_get_serial_port_data(port);
257
258 mutex_lock(&private->lock);
259 if (break_state) {
260 private->line_signals |= UPD78F0730_BREAK;
261 dev_dbg(dev, "%s - set BREAK\n", __func__);
262 } else {
263 private->line_signals &= ~UPD78F0730_BREAK;
264 dev_dbg(dev, "%s - clear BREAK\n", __func__);
265 }
266 request.opcode = UPD78F0730_CMD_SET_DTR_RTS;
267 request.params = private->line_signals;
268
269 upd78f0730_send_ctl(port, &request, sizeof(request));
270 mutex_unlock(&private->lock);
271}
272
273static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on)
274{
275 struct tty_struct *tty = port->port.tty;
276 unsigned int set = 0;
277 unsigned int clear = 0;
278
279 if (on)
280 set = TIOCM_DTR | TIOCM_RTS;
281 else
282 clear = TIOCM_DTR | TIOCM_RTS;
283
284 upd78f0730_tiocmset(tty, set, clear);
285}
286
287static speed_t upd78f0730_get_baud_rate(struct tty_struct *tty)
288{
289 const speed_t baud_rate = tty_get_baud_rate(tty);
290 const speed_t supported[] = {
291 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200
292 };
293 int i;
294
295 for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) {
296 if (baud_rate == supported[i])
297 return baud_rate;
298 }
299
300 /* If the baud rate is not supported, switch to the default one */
301 tty_encode_baud_rate(tty, 9600, 9600);
302
303 return tty_get_baud_rate(tty);
304}
305
306static void upd78f0730_set_termios(struct tty_struct *tty,
307 struct usb_serial_port *port,
308 struct ktermios *old_termios)
309{
310 struct device *dev = &port->dev;
311 struct upd78f0730_line_control request;
312 speed_t baud_rate;
313
314 if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))
315 return;
316
317 if (C_BAUD(tty) == B0)
318 upd78f0730_dtr_rts(port, 0);
319 else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
320 upd78f0730_dtr_rts(port, 1);
321
322 baud_rate = upd78f0730_get_baud_rate(tty);
323 request.opcode = UPD78F0730_CMD_LINE_CONTROL;
324 request.baud_rate = cpu_to_le32(baud_rate);
325 request.params = 0;
326 dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate);
327
328 switch (C_CSIZE(tty)) {
329 case CS7:
330 request.params |= UPD78F0730_DATA_SIZE_7_BITS;
331 dev_dbg(dev, "%s - 7 data bits\n", __func__);
332 break;
333 default:
334 tty->termios.c_cflag &= ~CSIZE;
335 tty->termios.c_cflag |= CS8;
336 dev_warn(dev, "data size is not supported, using 8 bits\n");
337 /* fall through */
338 case CS8:
339 request.params |= UPD78F0730_DATA_SIZE_8_BITS;
340 dev_dbg(dev, "%s - 8 data bits\n", __func__);
341 break;
342 }
343
344 if (C_PARENB(tty)) {
345 if (C_PARODD(tty)) {
346 request.params |= UPD78F0730_PARITY_ODD;
347 dev_dbg(dev, "%s - odd parity\n", __func__);
348 } else {
349 request.params |= UPD78F0730_PARITY_EVEN;
350 dev_dbg(dev, "%s - even parity\n", __func__);
351 }
352
353 if (C_CMSPAR(tty)) {
354 tty->termios.c_cflag &= ~CMSPAR;
355 dev_warn(dev, "MARK/SPACE parity is not supported\n");
356 }
357 } else {
358 request.params |= UPD78F0730_PARITY_NONE;
359 dev_dbg(dev, "%s - no parity\n", __func__);
360 }
361
362 if (C_CSTOPB(tty)) {
363 request.params |= UPD78F0730_STOP_BIT_2_BIT;
364 dev_dbg(dev, "%s - 2 stop bits\n", __func__);
365 } else {
366 request.params |= UPD78F0730_STOP_BIT_1_BIT;
367 dev_dbg(dev, "%s - 1 stop bit\n", __func__);
368 }
369
370 if (C_CRTSCTS(tty)) {
371 tty->termios.c_cflag &= ~CRTSCTS;
372 dev_warn(dev, "RTSCTS flow control is not supported\n");
373 }
374 if (I_IXOFF(tty) || I_IXON(tty)) {
375 tty->termios.c_iflag &= ~(IXOFF | IXON);
376 dev_warn(dev, "XON/XOFF flow control is not supported\n");
377 }
378 request.params |= UPD78F0730_FLOW_CONTROL_NONE;
379 dev_dbg(dev, "%s - no flow control\n", __func__);
380
381 upd78f0730_send_ctl(port, &request, sizeof(request));
382}
383
384static int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port)
385{
386 struct upd78f0730_open_close request = {
387 .opcode = UPD78F0730_CMD_OPEN_CLOSE,
388 .state = UPD78F0730_PORT_OPEN
389 };
390 int res;
391
392 res = upd78f0730_send_ctl(port, &request, sizeof(request));
393 if (res)
394 return res;
395
396 if (tty)
397 upd78f0730_set_termios(tty, port, NULL);
398
399 return usb_serial_generic_open(tty, port);
400}
401
402static void upd78f0730_close(struct usb_serial_port *port)
403{
404 struct upd78f0730_open_close request = {
405 .opcode = UPD78F0730_CMD_OPEN_CLOSE,
406 .state = UPD78F0730_PORT_CLOSE
407 };
408
409 usb_serial_generic_close(port);
410 upd78f0730_send_ctl(port, &request, sizeof(request));
411}
412
413static struct usb_serial_driver upd78f0730_device = {
414 .driver = {
415 .owner = THIS_MODULE,
416 .name = "upd78f0730",
417 },
418 .id_table = id_table,
419 .num_ports = 1,
420 .port_probe = upd78f0730_port_probe,
421 .port_remove = upd78f0730_port_remove,
422 .open = upd78f0730_open,
423 .close = upd78f0730_close,
424 .set_termios = upd78f0730_set_termios,
425 .tiocmget = upd78f0730_tiocmget,
426 .tiocmset = upd78f0730_tiocmset,
427 .dtr_rts = upd78f0730_dtr_rts,
428 .break_ctl = upd78f0730_break_ctl,
429};
430
431static struct usb_serial_driver * const serial_drivers[] = {
432 &upd78f0730_device,
433 NULL
434};
435
436module_usb_serial_driver(serial_drivers, id_table);
437
438MODULE_DESCRIPTION(DRIVER_DESC);
439MODULE_AUTHOR(DRIVER_AUTHOR);
440MODULE_LICENSE("GPL v2");