1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Navman Serial USB driver |
4 | * |
5 | * Copyright (C) 2006 Greg Kroah-Hartman <gregkh@suse.de> |
6 | * |
7 | * TODO: |
8 | * Add termios method that uses copy_hw but also kills all echo |
9 | * flags as the navman is rx only so cannot echo. |
10 | */ |
11 | |
12 | #include <linux/gfp.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/tty.h> |
15 | #include <linux/tty_flip.h> |
16 | #include <linux/module.h> |
17 | #include <linux/usb.h> |
18 | #include <linux/usb/serial.h> |
19 | |
20 | static const struct usb_device_id id_table[] = { |
21 | { USB_DEVICE(0x0a99, 0x0001) }, /* Talon Technology device */ |
22 | { USB_DEVICE(0x0df7, 0x0900) }, /* Mobile Action i-gotU */ |
23 | { }, |
24 | }; |
25 | MODULE_DEVICE_TABLE(usb, id_table); |
26 | |
27 | static void navman_read_int_callback(struct urb *urb) |
28 | { |
29 | struct usb_serial_port *port = urb->context; |
30 | unsigned char *data = urb->transfer_buffer; |
31 | int status = urb->status; |
32 | int result; |
33 | |
34 | switch (status) { |
35 | case 0: |
36 | /* success */ |
37 | break; |
38 | case -ECONNRESET: |
39 | case -ENOENT: |
40 | case -ESHUTDOWN: |
41 | /* this urb is terminated, clean up */ |
42 | dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n" , |
43 | __func__, status); |
44 | return; |
45 | default: |
46 | dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n" , |
47 | __func__, status); |
48 | goto exit; |
49 | } |
50 | |
51 | usb_serial_debug_data(dev: &port->dev, function: __func__, size: urb->actual_length, data); |
52 | |
53 | if (urb->actual_length) { |
54 | tty_insert_flip_string(port: &port->port, chars: data, size: urb->actual_length); |
55 | tty_flip_buffer_push(port: &port->port); |
56 | } |
57 | |
58 | exit: |
59 | result = usb_submit_urb(urb, GFP_ATOMIC); |
60 | if (result) |
61 | dev_err(&urb->dev->dev, |
62 | "%s - Error %d submitting interrupt urb\n" , |
63 | __func__, result); |
64 | } |
65 | |
66 | static int navman_open(struct tty_struct *tty, struct usb_serial_port *port) |
67 | { |
68 | int result = 0; |
69 | |
70 | if (port->interrupt_in_urb) { |
71 | dev_dbg(&port->dev, "%s - adding interrupt input for treo\n" , |
72 | __func__); |
73 | result = usb_submit_urb(urb: port->interrupt_in_urb, GFP_KERNEL); |
74 | if (result) |
75 | dev_err(&port->dev, |
76 | "%s - failed submitting interrupt urb, error %d\n" , |
77 | __func__, result); |
78 | } |
79 | return result; |
80 | } |
81 | |
82 | static void navman_close(struct usb_serial_port *port) |
83 | { |
84 | usb_kill_urb(urb: port->interrupt_in_urb); |
85 | } |
86 | |
87 | static int navman_write(struct tty_struct *tty, struct usb_serial_port *port, |
88 | const unsigned char *buf, int count) |
89 | { |
90 | /* |
91 | * This device can't write any data, only read from the device |
92 | */ |
93 | return -EOPNOTSUPP; |
94 | } |
95 | |
96 | static struct usb_serial_driver navman_device = { |
97 | .driver = { |
98 | .owner = THIS_MODULE, |
99 | .name = "navman" , |
100 | }, |
101 | .id_table = id_table, |
102 | .num_ports = 1, |
103 | .open = navman_open, |
104 | .close = navman_close, |
105 | .write = navman_write, |
106 | .read_int_callback = navman_read_int_callback, |
107 | }; |
108 | |
109 | static struct usb_serial_driver * const serial_drivers[] = { |
110 | &navman_device, NULL |
111 | }; |
112 | |
113 | module_usb_serial_driver(serial_drivers, id_table); |
114 | |
115 | MODULE_LICENSE("GPL v2" ); |
116 | |