1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * spcp8x5 USB to serial adaptor driver |
4 | * |
5 | * Copyright (C) 2010-2013 Johan Hovold (jhovold@gmail.com) |
6 | * Copyright (C) 2006 Linxb (xubin.lin@worldplus.com.cn) |
7 | * Copyright (C) 2006 S1 Corp. |
8 | * |
9 | * Original driver for 2.6.10 pl2303 driver by |
10 | * Greg Kroah-Hartman (greg@kroah.com) |
11 | * Changes for 2.6.20 by Harald Klein <hari@vt100.at> |
12 | */ |
13 | #include <linux/kernel.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/tty.h> |
17 | #include <linux/tty_driver.h> |
18 | #include <linux/tty_flip.h> |
19 | #include <linux/module.h> |
20 | #include <linux/spinlock.h> |
21 | #include <linux/usb.h> |
22 | #include <linux/usb/serial.h> |
23 | |
24 | #define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver" |
25 | |
26 | #define SPCP825_QUIRK_NO_UART_STATUS 0x01 |
27 | #define SPCP825_QUIRK_NO_WORK_MODE 0x02 |
28 | |
29 | #define SPCP8x5_007_VID 0x04FC |
30 | #define SPCP8x5_007_PID 0x0201 |
31 | #define SPCP8x5_008_VID 0x04fc |
32 | #define SPCP8x5_008_PID 0x0235 |
33 | #define SPCP8x5_PHILIPS_VID 0x0471 |
34 | #define SPCP8x5_PHILIPS_PID 0x081e |
35 | #define SPCP8x5_INTERMATIC_VID 0x04FC |
36 | #define SPCP8x5_INTERMATIC_PID 0x0204 |
37 | #define SPCP8x5_835_VID 0x04fc |
38 | #define SPCP8x5_835_PID 0x0231 |
39 | |
40 | static const struct usb_device_id id_table[] = { |
41 | { USB_DEVICE(SPCP8x5_PHILIPS_VID , SPCP8x5_PHILIPS_PID)}, |
42 | { USB_DEVICE(SPCP8x5_INTERMATIC_VID, SPCP8x5_INTERMATIC_PID)}, |
43 | { USB_DEVICE(SPCP8x5_835_VID, SPCP8x5_835_PID)}, |
44 | { USB_DEVICE(SPCP8x5_008_VID, SPCP8x5_008_PID)}, |
45 | { USB_DEVICE(SPCP8x5_007_VID, SPCP8x5_007_PID), |
46 | .driver_info = SPCP825_QUIRK_NO_UART_STATUS | |
47 | SPCP825_QUIRK_NO_WORK_MODE }, |
48 | { } /* Terminating entry */ |
49 | }; |
50 | MODULE_DEVICE_TABLE(usb, id_table); |
51 | |
52 | struct spcp8x5_usb_ctrl_arg { |
53 | u8 type; |
54 | u8 cmd; |
55 | u8 cmd_type; |
56 | u16 value; |
57 | u16 index; |
58 | u16 length; |
59 | }; |
60 | |
61 | |
62 | /* spcp8x5 spec register define */ |
63 | #define MCR_CONTROL_LINE_RTS 0x02 |
64 | #define MCR_CONTROL_LINE_DTR 0x01 |
65 | #define MCR_DTR 0x01 |
66 | #define MCR_RTS 0x02 |
67 | |
68 | #define MSR_STATUS_LINE_DCD 0x80 |
69 | #define MSR_STATUS_LINE_RI 0x40 |
70 | #define MSR_STATUS_LINE_DSR 0x20 |
71 | #define MSR_STATUS_LINE_CTS 0x10 |
72 | |
73 | /* verdor command here , we should define myself */ |
74 | #define SET_DEFAULT 0x40 |
75 | #define SET_DEFAULT_TYPE 0x20 |
76 | |
77 | #define SET_UART_FORMAT 0x40 |
78 | #define SET_UART_FORMAT_TYPE 0x21 |
79 | #define SET_UART_FORMAT_SIZE_5 0x00 |
80 | #define SET_UART_FORMAT_SIZE_6 0x01 |
81 | #define SET_UART_FORMAT_SIZE_7 0x02 |
82 | #define SET_UART_FORMAT_SIZE_8 0x03 |
83 | #define SET_UART_FORMAT_STOP_1 0x00 |
84 | #define SET_UART_FORMAT_STOP_2 0x04 |
85 | #define SET_UART_FORMAT_PAR_NONE 0x00 |
86 | #define SET_UART_FORMAT_PAR_ODD 0x10 |
87 | #define SET_UART_FORMAT_PAR_EVEN 0x30 |
88 | #define SET_UART_FORMAT_PAR_MASK 0xD0 |
89 | #define SET_UART_FORMAT_PAR_SPACE 0x90 |
90 | |
91 | #define GET_UART_STATUS_TYPE 0xc0 |
92 | #define GET_UART_STATUS 0x22 |
93 | #define GET_UART_STATUS_MSR 0x06 |
94 | |
95 | #define SET_UART_STATUS 0x40 |
96 | #define SET_UART_STATUS_TYPE 0x23 |
97 | #define SET_UART_STATUS_MCR 0x0004 |
98 | #define SET_UART_STATUS_MCR_DTR 0x01 |
99 | #define SET_UART_STATUS_MCR_RTS 0x02 |
100 | #define SET_UART_STATUS_MCR_LOOP 0x10 |
101 | |
102 | #define SET_WORKING_MODE 0x40 |
103 | #define SET_WORKING_MODE_TYPE 0x24 |
104 | #define SET_WORKING_MODE_U2C 0x00 |
105 | #define SET_WORKING_MODE_RS485 0x01 |
106 | #define SET_WORKING_MODE_PDMA 0x02 |
107 | #define SET_WORKING_MODE_SPP 0x03 |
108 | |
109 | #define SET_FLOWCTL_CHAR 0x40 |
110 | #define SET_FLOWCTL_CHAR_TYPE 0x25 |
111 | |
112 | #define GET_VERSION 0xc0 |
113 | #define GET_VERSION_TYPE 0x26 |
114 | |
115 | #define SET_REGISTER 0x40 |
116 | #define SET_REGISTER_TYPE 0x27 |
117 | |
118 | #define GET_REGISTER 0xc0 |
119 | #define GET_REGISTER_TYPE 0x28 |
120 | |
121 | #define SET_RAM 0x40 |
122 | #define SET_RAM_TYPE 0x31 |
123 | |
124 | #define GET_RAM 0xc0 |
125 | #define GET_RAM_TYPE 0x32 |
126 | |
127 | /* how come ??? */ |
128 | #define UART_STATE 0x08 |
129 | #define UART_STATE_TRANSIENT_MASK 0x75 |
130 | #define UART_DCD 0x01 |
131 | #define UART_DSR 0x02 |
132 | #define UART_BREAK_ERROR 0x04 |
133 | #define UART_RING 0x08 |
134 | #define UART_FRAME_ERROR 0x10 |
135 | #define UART_PARITY_ERROR 0x20 |
136 | #define UART_OVERRUN_ERROR 0x40 |
137 | #define UART_CTS 0x80 |
138 | |
139 | struct spcp8x5_private { |
140 | unsigned quirks; |
141 | spinlock_t lock; |
142 | u8 line_control; |
143 | }; |
144 | |
145 | static int spcp8x5_probe(struct usb_serial *serial, |
146 | const struct usb_device_id *id) |
147 | { |
148 | usb_set_serial_data(serial, data: (void *)id); |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | static int spcp8x5_port_probe(struct usb_serial_port *port) |
154 | { |
155 | const struct usb_device_id *id = usb_get_serial_data(serial: port->serial); |
156 | struct spcp8x5_private *priv; |
157 | |
158 | priv = kzalloc(size: sizeof(*priv), GFP_KERNEL); |
159 | if (!priv) |
160 | return -ENOMEM; |
161 | |
162 | spin_lock_init(&priv->lock); |
163 | priv->quirks = id->driver_info; |
164 | |
165 | usb_set_serial_port_data(port, data: priv); |
166 | |
167 | port->port.drain_delay = 256; |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static void spcp8x5_port_remove(struct usb_serial_port *port) |
173 | { |
174 | struct spcp8x5_private *priv; |
175 | |
176 | priv = usb_get_serial_port_data(port); |
177 | kfree(objp: priv); |
178 | } |
179 | |
180 | static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) |
181 | { |
182 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
183 | struct usb_device *dev = port->serial->dev; |
184 | int retval; |
185 | |
186 | if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) |
187 | return -EPERM; |
188 | |
189 | retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
190 | SET_UART_STATUS_TYPE, SET_UART_STATUS, |
191 | value: mcr, index: 0x04, NULL, size: 0, timeout: 100); |
192 | if (retval != 0) { |
193 | dev_err(&port->dev, "failed to set control lines: %d\n" , |
194 | retval); |
195 | } |
196 | return retval; |
197 | } |
198 | |
199 | static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) |
200 | { |
201 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
202 | struct usb_device *dev = port->serial->dev; |
203 | u8 *buf; |
204 | int ret; |
205 | |
206 | if (priv->quirks & SPCP825_QUIRK_NO_UART_STATUS) |
207 | return -EPERM; |
208 | |
209 | buf = kzalloc(size: 1, GFP_KERNEL); |
210 | if (!buf) |
211 | return -ENOMEM; |
212 | |
213 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
214 | GET_UART_STATUS, GET_UART_STATUS_TYPE, |
215 | value: 0, GET_UART_STATUS_MSR, data: buf, size: 1, timeout: 100); |
216 | if (ret < 1) { |
217 | dev_err(&port->dev, "failed to get modem status: %d\n" , ret); |
218 | if (ret >= 0) |
219 | ret = -EIO; |
220 | goto out; |
221 | } |
222 | |
223 | dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n" , ret, *buf); |
224 | *status = *buf; |
225 | ret = 0; |
226 | out: |
227 | kfree(objp: buf); |
228 | |
229 | return ret; |
230 | } |
231 | |
232 | static void spcp8x5_set_work_mode(struct usb_serial_port *port, u16 value, |
233 | u16 index) |
234 | { |
235 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
236 | struct usb_device *dev = port->serial->dev; |
237 | int ret; |
238 | |
239 | if (priv->quirks & SPCP825_QUIRK_NO_WORK_MODE) |
240 | return; |
241 | |
242 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
243 | SET_WORKING_MODE_TYPE, SET_WORKING_MODE, |
244 | value, index, NULL, size: 0, timeout: 100); |
245 | dev_dbg(&port->dev, "value = %#x , index = %#x\n" , value, index); |
246 | if (ret < 0) |
247 | dev_err(&port->dev, "failed to set work mode: %d\n" , ret); |
248 | } |
249 | |
250 | static int spcp8x5_carrier_raised(struct usb_serial_port *port) |
251 | { |
252 | u8 msr; |
253 | int ret; |
254 | |
255 | ret = spcp8x5_get_msr(port, status: &msr); |
256 | if (ret || msr & MSR_STATUS_LINE_DCD) |
257 | return 1; |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) |
263 | { |
264 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
265 | unsigned long flags; |
266 | u8 control; |
267 | |
268 | spin_lock_irqsave(&priv->lock, flags); |
269 | if (on) |
270 | priv->line_control = MCR_CONTROL_LINE_DTR |
271 | | MCR_CONTROL_LINE_RTS; |
272 | else |
273 | priv->line_control &= ~ (MCR_CONTROL_LINE_DTR |
274 | | MCR_CONTROL_LINE_RTS); |
275 | control = priv->line_control; |
276 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
277 | spcp8x5_set_ctrl_line(port, mcr: control); |
278 | } |
279 | |
280 | static void spcp8x5_init_termios(struct tty_struct *tty) |
281 | { |
282 | tty_encode_baud_rate(tty, ibaud: 115200, obaud: 115200); |
283 | } |
284 | |
285 | static void spcp8x5_set_termios(struct tty_struct *tty, |
286 | struct usb_serial_port *port, |
287 | const struct ktermios *old_termios) |
288 | { |
289 | struct usb_serial *serial = port->serial; |
290 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
291 | unsigned long flags; |
292 | unsigned int cflag = tty->termios.c_cflag; |
293 | unsigned short uartdata; |
294 | unsigned char buf[2] = {0, 0}; |
295 | int baud; |
296 | int i; |
297 | u8 control; |
298 | |
299 | /* check that they really want us to change something */ |
300 | if (old_termios && !tty_termios_hw_change(a: &tty->termios, b: old_termios)) |
301 | return; |
302 | |
303 | /* set DTR/RTS active */ |
304 | spin_lock_irqsave(&priv->lock, flags); |
305 | control = priv->line_control; |
306 | if (old_termios && (old_termios->c_cflag & CBAUD) == B0) { |
307 | priv->line_control |= MCR_DTR; |
308 | if (!(old_termios->c_cflag & CRTSCTS)) |
309 | priv->line_control |= MCR_RTS; |
310 | } |
311 | if (control != priv->line_control) { |
312 | control = priv->line_control; |
313 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
314 | spcp8x5_set_ctrl_line(port, mcr: control); |
315 | } else { |
316 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
317 | } |
318 | |
319 | /* Set Baud Rate */ |
320 | baud = tty_get_baud_rate(tty); |
321 | switch (baud) { |
322 | case 300: buf[0] = 0x00; break; |
323 | case 600: buf[0] = 0x01; break; |
324 | case 1200: buf[0] = 0x02; break; |
325 | case 2400: buf[0] = 0x03; break; |
326 | case 4800: buf[0] = 0x04; break; |
327 | case 9600: buf[0] = 0x05; break; |
328 | case 19200: buf[0] = 0x07; break; |
329 | case 38400: buf[0] = 0x09; break; |
330 | case 57600: buf[0] = 0x0a; break; |
331 | case 115200: buf[0] = 0x0b; break; |
332 | case 230400: buf[0] = 0x0c; break; |
333 | case 460800: buf[0] = 0x0d; break; |
334 | case 921600: buf[0] = 0x0e; break; |
335 | /* case 1200000: buf[0] = 0x0f; break; */ |
336 | /* case 2400000: buf[0] = 0x10; break; */ |
337 | case 3000000: buf[0] = 0x11; break; |
338 | /* case 6000000: buf[0] = 0x12; break; */ |
339 | case 0: |
340 | case 1000000: |
341 | buf[0] = 0x0b; break; |
342 | default: |
343 | dev_err(&port->dev, "unsupported baudrate, using 9600\n" ); |
344 | } |
345 | |
346 | /* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */ |
347 | switch (cflag & CSIZE) { |
348 | case CS5: |
349 | buf[1] |= SET_UART_FORMAT_SIZE_5; |
350 | break; |
351 | case CS6: |
352 | buf[1] |= SET_UART_FORMAT_SIZE_6; |
353 | break; |
354 | case CS7: |
355 | buf[1] |= SET_UART_FORMAT_SIZE_7; |
356 | break; |
357 | default: |
358 | case CS8: |
359 | buf[1] |= SET_UART_FORMAT_SIZE_8; |
360 | break; |
361 | } |
362 | |
363 | /* Set Stop bit2 : 0:1bit 1:2bit */ |
364 | buf[1] |= (cflag & CSTOPB) ? SET_UART_FORMAT_STOP_2 : |
365 | SET_UART_FORMAT_STOP_1; |
366 | |
367 | /* Set Parity bit3-4 01:Odd 11:Even */ |
368 | if (cflag & PARENB) { |
369 | buf[1] |= (cflag & PARODD) ? |
370 | SET_UART_FORMAT_PAR_ODD : SET_UART_FORMAT_PAR_EVEN ; |
371 | } else { |
372 | buf[1] |= SET_UART_FORMAT_PAR_NONE; |
373 | } |
374 | uartdata = buf[0] | buf[1]<<8; |
375 | |
376 | i = usb_control_msg(dev: serial->dev, usb_sndctrlpipe(serial->dev, 0), |
377 | SET_UART_FORMAT_TYPE, SET_UART_FORMAT, |
378 | value: uartdata, index: 0, NULL, size: 0, timeout: 100); |
379 | if (i < 0) |
380 | dev_err(&port->dev, "Set UART format %#x failed (error = %d)\n" , |
381 | uartdata, i); |
382 | dev_dbg(&port->dev, "0x21:0x40:0:0 %d\n" , i); |
383 | |
384 | if (cflag & CRTSCTS) { |
385 | /* enable hardware flow control */ |
386 | spcp8x5_set_work_mode(port, value: 0x000a, SET_WORKING_MODE_U2C); |
387 | } |
388 | } |
389 | |
390 | static int spcp8x5_open(struct tty_struct *tty, struct usb_serial_port *port) |
391 | { |
392 | struct usb_serial *serial = port->serial; |
393 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
394 | int ret; |
395 | |
396 | usb_clear_halt(dev: serial->dev, pipe: port->write_urb->pipe); |
397 | usb_clear_halt(dev: serial->dev, pipe: port->read_urb->pipe); |
398 | |
399 | ret = usb_control_msg(dev: serial->dev, usb_sndctrlpipe(serial->dev, 0), |
400 | request: 0x09, requesttype: 0x00, |
401 | value: 0x01, index: 0x00, NULL, size: 0x00, timeout: 100); |
402 | if (ret) |
403 | return ret; |
404 | |
405 | spcp8x5_set_ctrl_line(port, mcr: priv->line_control); |
406 | |
407 | if (tty) |
408 | spcp8x5_set_termios(tty, port, NULL); |
409 | |
410 | return usb_serial_generic_open(tty, port); |
411 | } |
412 | |
413 | static int spcp8x5_tiocmset(struct tty_struct *tty, |
414 | unsigned int set, unsigned int clear) |
415 | { |
416 | struct usb_serial_port *port = tty->driver_data; |
417 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
418 | unsigned long flags; |
419 | u8 control; |
420 | |
421 | spin_lock_irqsave(&priv->lock, flags); |
422 | if (set & TIOCM_RTS) |
423 | priv->line_control |= MCR_RTS; |
424 | if (set & TIOCM_DTR) |
425 | priv->line_control |= MCR_DTR; |
426 | if (clear & TIOCM_RTS) |
427 | priv->line_control &= ~MCR_RTS; |
428 | if (clear & TIOCM_DTR) |
429 | priv->line_control &= ~MCR_DTR; |
430 | control = priv->line_control; |
431 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
432 | |
433 | return spcp8x5_set_ctrl_line(port, mcr: control); |
434 | } |
435 | |
436 | static int spcp8x5_tiocmget(struct tty_struct *tty) |
437 | { |
438 | struct usb_serial_port *port = tty->driver_data; |
439 | struct spcp8x5_private *priv = usb_get_serial_port_data(port); |
440 | unsigned long flags; |
441 | unsigned int mcr; |
442 | u8 status; |
443 | unsigned int result; |
444 | |
445 | result = spcp8x5_get_msr(port, status: &status); |
446 | if (result) |
447 | return result; |
448 | |
449 | spin_lock_irqsave(&priv->lock, flags); |
450 | mcr = priv->line_control; |
451 | spin_unlock_irqrestore(lock: &priv->lock, flags); |
452 | |
453 | result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) |
454 | | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) |
455 | | ((status & MSR_STATUS_LINE_CTS) ? TIOCM_CTS : 0) |
456 | | ((status & MSR_STATUS_LINE_DSR) ? TIOCM_DSR : 0) |
457 | | ((status & MSR_STATUS_LINE_RI) ? TIOCM_RI : 0) |
458 | | ((status & MSR_STATUS_LINE_DCD) ? TIOCM_CD : 0); |
459 | |
460 | return result; |
461 | } |
462 | |
463 | static struct usb_serial_driver spcp8x5_device = { |
464 | .driver = { |
465 | .owner = THIS_MODULE, |
466 | .name = "SPCP8x5" , |
467 | }, |
468 | .id_table = id_table, |
469 | .num_ports = 1, |
470 | .num_bulk_in = 1, |
471 | .num_bulk_out = 1, |
472 | .open = spcp8x5_open, |
473 | .dtr_rts = spcp8x5_dtr_rts, |
474 | .carrier_raised = spcp8x5_carrier_raised, |
475 | .set_termios = spcp8x5_set_termios, |
476 | .init_termios = spcp8x5_init_termios, |
477 | .tiocmget = spcp8x5_tiocmget, |
478 | .tiocmset = spcp8x5_tiocmset, |
479 | .probe = spcp8x5_probe, |
480 | .port_probe = spcp8x5_port_probe, |
481 | .port_remove = spcp8x5_port_remove, |
482 | }; |
483 | |
484 | static struct usb_serial_driver * const serial_drivers[] = { |
485 | &spcp8x5_device, NULL |
486 | }; |
487 | |
488 | module_usb_serial_driver(serial_drivers, id_table); |
489 | |
490 | MODULE_DESCRIPTION(DRIVER_DESC); |
491 | MODULE_LICENSE("GPL" ); |
492 | |