Blame view

drivers/usb/serial/digi_acceleport.c 43.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
  *  Digi AccelePort USB-4 and USB-2 Serial Converters
  *
  *  Copyright 2000 by Digi International
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's
  *  usb-serial driver.
  *
  *  Peter Berger (pberger@brimson.com)
  *  Al Borchers (borchers@steinerpoint.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
  #include <linux/kernel.h>
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
  #include <linux/slab.h>
  #include <linux/tty.h>
  #include <linux/tty_driver.h>
  #include <linux/tty_flip.h>
  #include <linux/module.h>
  #include <linux/spinlock.h>
  #include <linux/workqueue.h>
41ad427da   Alan Cox   digi_acceleport: ...
26
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  #include <linux/usb.h>
  #include <linux/wait.h>
174cd4b1e   Ingo Molnar   sched/headers: Pr...
29
  #include <linux/sched/signal.h>
a969888ce   Greg Kroah-Hartman   [PATCH] USB: move...
30
  #include <linux/usb/serial.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
  
  /* Defines */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  #define DRIVER_AUTHOR "Peter Berger <pberger@brimson.com>, Al Borchers <borchers@steinerpoint.com>"
  #define DRIVER_DESC "Digi AccelePort USB-2/USB-4 Serial Converter driver"
  
  /* port output buffer length -- must be <= transfer buffer length - 2 */
  /* so we can be sure to send the full buffer in one urb */
  #define DIGI_OUT_BUF_SIZE		8
  
  /* port input buffer length -- must be >= transfer buffer length - 3 */
  /* so we can be sure to hold at least one full buffer from one urb */
  #define DIGI_IN_BUF_SIZE		64
  
  /* retry timeout while sleeping */
  #define DIGI_RETRY_TIMEOUT		(HZ/10)
  
  /* timeout while waiting for tty output to drain in close */
  /* this delay is used twice in close, so the total delay could */
  /* be twice this value */
  #define DIGI_CLOSE_TIMEOUT		(5*HZ)
  
  
  /* AccelePort USB Defines */
  
  /* ids */
  #define DIGI_VENDOR_ID			0x05c5
  #define DIGI_2_ID			0x0002	/* USB-2 */
  #define DIGI_4_ID			0x0004	/* USB-4 */
  
  /* commands
   * "INB": can be used on the in-band endpoint
   * "OOB": can be used on the out-of-band endpoint
   */
  #define DIGI_CMD_SET_BAUD_RATE			0	/* INB, OOB */
  #define DIGI_CMD_SET_WORD_SIZE			1	/* INB, OOB */
  #define DIGI_CMD_SET_PARITY			2	/* INB, OOB */
  #define DIGI_CMD_SET_STOP_BITS			3	/* INB, OOB */
  #define DIGI_CMD_SET_INPUT_FLOW_CONTROL		4	/* INB, OOB */
  #define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL	5	/* INB, OOB */
  #define DIGI_CMD_SET_DTR_SIGNAL			6	/* INB, OOB */
  #define DIGI_CMD_SET_RTS_SIGNAL			7	/* INB, OOB */
  #define DIGI_CMD_READ_INPUT_SIGNALS		8	/*      OOB */
  #define DIGI_CMD_IFLUSH_FIFO			9	/*      OOB */
  #define DIGI_CMD_RECEIVE_ENABLE			10	/* INB, OOB */
  #define DIGI_CMD_BREAK_CONTROL			11	/* INB, OOB */
  #define DIGI_CMD_LOCAL_LOOPBACK			12	/* INB, OOB */
  #define DIGI_CMD_TRANSMIT_IDLE			13	/* INB, OOB */
  #define DIGI_CMD_READ_UART_REGISTER		14	/*      OOB */
  #define DIGI_CMD_WRITE_UART_REGISTER		15	/* INB, OOB */
  #define DIGI_CMD_AND_UART_REGISTER		16	/* INB, OOB */
  #define DIGI_CMD_OR_UART_REGISTER		17	/* INB, OOB */
  #define DIGI_CMD_SEND_DATA			18	/* INB      */
  #define DIGI_CMD_RECEIVE_DATA			19	/* INB      */
  #define DIGI_CMD_RECEIVE_DISABLE		20	/* INB      */
  #define DIGI_CMD_GET_PORT_TYPE			21	/*      OOB */
  
  /* baud rates */
  #define DIGI_BAUD_50				0
  #define DIGI_BAUD_75				1
  #define DIGI_BAUD_110				2
  #define DIGI_BAUD_150				3
  #define DIGI_BAUD_200				4
  #define DIGI_BAUD_300				5
  #define DIGI_BAUD_600				6
  #define DIGI_BAUD_1200				7
  #define DIGI_BAUD_1800				8
  #define DIGI_BAUD_2400				9
  #define DIGI_BAUD_4800				10
  #define DIGI_BAUD_7200				11
  #define DIGI_BAUD_9600				12
  #define DIGI_BAUD_14400				13
  #define DIGI_BAUD_19200				14
  #define DIGI_BAUD_28800				15
  #define DIGI_BAUD_38400				16
  #define DIGI_BAUD_57600				17
  #define DIGI_BAUD_76800				18
  #define DIGI_BAUD_115200			19
  #define DIGI_BAUD_153600			20
  #define DIGI_BAUD_230400			21
  #define DIGI_BAUD_460800			22
  
  /* arguments */
  #define DIGI_WORD_SIZE_5			0
  #define DIGI_WORD_SIZE_6			1
  #define DIGI_WORD_SIZE_7			2
  #define DIGI_WORD_SIZE_8			3
  
  #define DIGI_PARITY_NONE			0
  #define DIGI_PARITY_ODD				1
  #define DIGI_PARITY_EVEN			2
  #define DIGI_PARITY_MARK			3
  #define DIGI_PARITY_SPACE			4
  
  #define DIGI_STOP_BITS_1			0
  #define DIGI_STOP_BITS_2			1
  
  #define DIGI_INPUT_FLOW_CONTROL_XON_XOFF	1
  #define DIGI_INPUT_FLOW_CONTROL_RTS		2
  #define DIGI_INPUT_FLOW_CONTROL_DTR		4
  
  #define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF	1
  #define DIGI_OUTPUT_FLOW_CONTROL_CTS		2
  #define DIGI_OUTPUT_FLOW_CONTROL_DSR		4
  
  #define DIGI_DTR_INACTIVE			0
  #define DIGI_DTR_ACTIVE				1
  #define DIGI_DTR_INPUT_FLOW_CONTROL		2
  
  #define DIGI_RTS_INACTIVE			0
  #define DIGI_RTS_ACTIVE				1
  #define DIGI_RTS_INPUT_FLOW_CONTROL		2
  #define DIGI_RTS_TOGGLE				3
  
  #define DIGI_FLUSH_TX				1
  #define DIGI_FLUSH_RX				2
  #define DIGI_RESUME_TX				4 /* clears xoff condition */
  
  #define DIGI_TRANSMIT_NOT_IDLE			0
  #define DIGI_TRANSMIT_IDLE			1
  
  #define DIGI_DISABLE				0
  #define DIGI_ENABLE				1
  
  #define DIGI_DEASSERT				0
  #define DIGI_ASSERT				1
  
  /* in band status codes */
  #define DIGI_OVERRUN_ERROR			4
  #define DIGI_PARITY_ERROR			8
  #define DIGI_FRAMING_ERROR			16
  #define DIGI_BREAK_ERROR			32
  
  /* out of band status */
  #define DIGI_NO_ERROR				0
  #define DIGI_BAD_FIRST_PARAMETER		1
  #define DIGI_BAD_SECOND_PARAMETER		2
  #define DIGI_INVALID_LINE			3
  #define DIGI_INVALID_OPCODE			4
  
  /* input signals */
  #define DIGI_READ_INPUT_SIGNALS_SLOT		1
  #define DIGI_READ_INPUT_SIGNALS_ERR		2
  #define DIGI_READ_INPUT_SIGNALS_BUSY		4
  #define DIGI_READ_INPUT_SIGNALS_PE		8
  #define DIGI_READ_INPUT_SIGNALS_CTS		16
  #define DIGI_READ_INPUT_SIGNALS_DSR		32
  #define DIGI_READ_INPUT_SIGNALS_RI		64
  #define DIGI_READ_INPUT_SIGNALS_DCD		128
  
  
  /* Structures */
  
  struct digi_serial {
  	spinlock_t ds_serial_lock;
  	struct usb_serial_port *ds_oob_port;	/* out-of-band port */
  	int ds_oob_port_num;			/* index of out-of-band port */
  	int ds_device_started;
  };
  
  struct digi_port {
  	spinlock_t dp_port_lock;
  	int dp_port_num;
  	int dp_out_buf_len;
  	unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
  	int dp_write_urb_in_use;
  	unsigned int dp_modem_signals;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
201
  	int dp_transmit_idle;
  	wait_queue_head_t dp_transmit_idle_wait;
  	int dp_throttled;
  	int dp_throttle_restart;
  	wait_queue_head_t dp_flush_wait;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
  	wait_queue_head_t dp_close_wait;	/* wait queue for close */
  	struct work_struct dp_wakeup_work;
c4028958b   David Howells   WorkStruct: make ...
204
  	struct usb_serial_port *dp_port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
  };
  
  
  /* Local Function Declarations */
c4028958b   David Howells   WorkStruct: make ...
209
  static void digi_wakeup_write_lock(struct work_struct *work);
c6d61269f   Alan Cox   digi_acceleport: ...
210
211
212
213
214
215
216
217
  static int digi_write_oob_command(struct usb_serial_port *port,
  	unsigned char *buf, int count, int interruptible);
  static int digi_write_inb_command(struct usb_serial_port *port,
  	unsigned char *buf, int count, unsigned long timeout);
  static int digi_set_modem_signals(struct usb_serial_port *port,
  	unsigned int modem_signals, int interruptible);
  static int digi_transmit_idle(struct usb_serial_port *port,
  	unsigned long timeout);
41ad427da   Alan Cox   digi_acceleport: ...
218
219
  static void digi_rx_throttle(struct tty_struct *tty);
  static void digi_rx_unthrottle(struct tty_struct *tty);
95da310e6   Alan Cox   usb_serial: API a...
220
221
222
  static void digi_set_termios(struct tty_struct *tty,
  		struct usb_serial_port *port, struct ktermios *old_termios);
  static void digi_break_ctl(struct tty_struct *tty, int break_state);
60b33c133   Alan Cox   tiocmget: kill of...
223
  static int digi_tiocmget(struct tty_struct *tty);
20b9d1771   Alan Cox   tiocmset: kill th...
224
225
  static int digi_tiocmset(struct tty_struct *tty, unsigned int set,
  		unsigned int clear);
95da310e6   Alan Cox   usb_serial: API a...
226
  static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
20b9d1771   Alan Cox   tiocmset: kill th...
227
  		const unsigned char *buf, int count);
c6d61269f   Alan Cox   digi_acceleport: ...
228
  static void digi_write_bulk_callback(struct urb *urb);
95da310e6   Alan Cox   usb_serial: API a...
229
230
  static int digi_write_room(struct tty_struct *tty);
  static int digi_chars_in_buffer(struct tty_struct *tty);
a509a7e47   Alan Cox   tty: USB does not...
231
  static int digi_open(struct tty_struct *tty, struct usb_serial_port *port);
335f8514f   Alan Cox   tty: Bring the us...
232
  static void digi_close(struct usb_serial_port *port);
335f8514f   Alan Cox   tty: Bring the us...
233
  static void digi_dtr_rts(struct usb_serial_port *port, int on);
c6d61269f   Alan Cox   digi_acceleport: ...
234
235
  static int digi_startup_device(struct usb_serial *serial);
  static int digi_startup(struct usb_serial *serial);
f9c99bb8b   Alan Stern   USB: usb-serial: ...
236
237
  static void digi_disconnect(struct usb_serial *serial);
  static void digi_release(struct usb_serial *serial);
fb44ff854   Johan Hovold   USB: digi_accelep...
238
239
  static int digi_port_probe(struct usb_serial_port *port);
  static int digi_port_remove(struct usb_serial_port *port);
c6d61269f   Alan Cox   digi_acceleport: ...
240
241
242
  static void digi_read_bulk_callback(struct urb *urb);
  static int digi_read_inb_callback(struct urb *urb);
  static int digi_read_oob_callback(struct urb *urb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243

7d40d7e85   Németh Márton   USB serial: make ...
244
  static const struct usb_device_id id_table_combined[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
  	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
  	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
  	{ }						/* Terminating entry */
  };
7d40d7e85   Németh Márton   USB serial: make ...
249
  static const struct usb_device_id id_table_2[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
  	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
  	{ }						/* Terminating entry */
  };
7d40d7e85   Németh Márton   USB serial: make ...
253
  static const struct usb_device_id id_table_4[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
  	{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
  	{ }						/* Terminating entry */
  };
41ad427da   Alan Cox   digi_acceleport: ...
257
  MODULE_DEVICE_TABLE(usb, id_table_combined);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  /* device info needed for the Digi serial converter */
ea65370d0   Greg Kroah-Hartman   [PATCH] USB Seria...
260
  static struct usb_serial_driver digi_acceleport_2_device = {
18fcac353   Greg Kroah-Hartman   [PATCH] USB Seria...
261
  	.driver = {
269bda1c1   Greg Kroah-Hartman   [PATCH] USB Seria...
262
263
  		.owner =		THIS_MODULE,
  		.name =			"digi_2",
18fcac353   Greg Kroah-Hartman   [PATCH] USB Seria...
264
  	},
269bda1c1   Greg Kroah-Hartman   [PATCH] USB Seria...
265
  	.description =			"Digi 2 port USB adapter",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
  	.id_table =			id_table_2,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  	.num_ports =			3,
e7d6507e5   Johan Hovold   USB: serial: digi...
268
269
  	.num_bulk_in =			4,
  	.num_bulk_out =			4,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
  	.open =				digi_open,
  	.close =			digi_close,
335f8514f   Alan Cox   tty: Bring the us...
272
  	.dtr_rts =			digi_dtr_rts,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
  	.write =			digi_write,
  	.write_room =			digi_write_room,
  	.write_bulk_callback = 		digi_write_bulk_callback,
  	.read_bulk_callback =		digi_read_bulk_callback,
  	.chars_in_buffer =		digi_chars_in_buffer,
  	.throttle =			digi_rx_throttle,
  	.unthrottle =			digi_rx_unthrottle,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
  	.set_termios =			digi_set_termios,
  	.break_ctl =			digi_break_ctl,
  	.tiocmget =			digi_tiocmget,
  	.tiocmset =			digi_tiocmset,
  	.attach =			digi_startup,
f9c99bb8b   Alan Stern   USB: usb-serial: ...
285
286
  	.disconnect =			digi_disconnect,
  	.release =			digi_release,
fb44ff854   Johan Hovold   USB: digi_accelep...
287
288
  	.port_probe =			digi_port_probe,
  	.port_remove =			digi_port_remove,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  };
ea65370d0   Greg Kroah-Hartman   [PATCH] USB Seria...
290
  static struct usb_serial_driver digi_acceleport_4_device = {
18fcac353   Greg Kroah-Hartman   [PATCH] USB Seria...
291
  	.driver = {
269bda1c1   Greg Kroah-Hartman   [PATCH] USB Seria...
292
293
  		.owner =		THIS_MODULE,
  		.name =			"digi_4",
18fcac353   Greg Kroah-Hartman   [PATCH] USB Seria...
294
  	},
269bda1c1   Greg Kroah-Hartman   [PATCH] USB Seria...
295
  	.description =			"Digi 4 port USB adapter",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
  	.id_table =			id_table_4,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  	.num_ports =			4,
e7d6507e5   Johan Hovold   USB: serial: digi...
298
299
  	.num_bulk_in =			5,
  	.num_bulk_out =			5,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
306
307
308
  	.open =				digi_open,
  	.close =			digi_close,
  	.write =			digi_write,
  	.write_room =			digi_write_room,
  	.write_bulk_callback = 		digi_write_bulk_callback,
  	.read_bulk_callback =		digi_read_bulk_callback,
  	.chars_in_buffer =		digi_chars_in_buffer,
  	.throttle =			digi_rx_throttle,
  	.unthrottle =			digi_rx_unthrottle,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
312
313
  	.set_termios =			digi_set_termios,
  	.break_ctl =			digi_break_ctl,
  	.tiocmget =			digi_tiocmget,
  	.tiocmset =			digi_tiocmset,
  	.attach =			digi_startup,
f9c99bb8b   Alan Stern   USB: usb-serial: ...
314
315
  	.disconnect =			digi_disconnect,
  	.release =			digi_release,
fb44ff854   Johan Hovold   USB: digi_accelep...
316
317
  	.port_probe =			digi_port_probe,
  	.port_remove =			digi_port_remove,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  };
97b6b6d23   Alan Stern   usb-serial: use n...
319
320
321
  static struct usb_serial_driver * const serial_drivers[] = {
  	&digi_acceleport_2_device, &digi_acceleport_4_device, NULL
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
325
  
  /* Functions */
  
  /*
41ad427da   Alan Cox   digi_acceleport: ...
326
327
328
329
330
331
332
333
334
335
336
   *  Cond Wait Interruptible Timeout Irqrestore
   *
   *  Do spin_unlock_irqrestore and interruptible_sleep_on_timeout
   *  so that wake ups are not lost if they occur between the unlock
   *  and the sleep.  In other words, spin_unlock_irqrestore and
   *  interruptible_sleep_on_timeout are "atomic" with respect to
   *  wake ups.  This is used to implement condition variables.
   *
   *  interruptible_sleep_on_timeout is deprecated and has been replaced
   *  with the equivalent code.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337

c6d61269f   Alan Cox   digi_acceleport: ...
338
  static long cond_wait_interruptible_timeout_irqrestore(
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  	wait_queue_head_t *q, long timeout,
c6d61269f   Alan Cox   digi_acceleport: ...
340
  	spinlock_t *lock, unsigned long flags)
5a33956a3   Harvey Harrison   USB: digi_accelpo...
341
  __releases(lock)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
  {
  	DEFINE_WAIT(wait);
45f23f189   Nishanth Aravamudan   [PATCH] USB: usb/...
344
  	prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
349
  	spin_unlock_irqrestore(lock, flags);
  	timeout = schedule_timeout(timeout);
  	finish_wait(q, &wait);
  
  	return timeout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
  }
  
  
  /*
41ad427da   Alan Cox   digi_acceleport: ...
354
355
356
357
358
   *  Digi Wakeup Write
   *
   *  Wake up port, line discipline, and tty processes sleeping
   *  on writes.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359

c4028958b   David Howells   WorkStruct: make ...
360
  static void digi_wakeup_write_lock(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  {
41ad427da   Alan Cox   digi_acceleport: ...
362
363
  	struct digi_port *priv =
  			container_of(work, struct digi_port, dp_wakeup_work);
c4028958b   David Howells   WorkStruct: make ...
364
  	struct usb_serial_port *port = priv->dp_port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

c6d61269f   Alan Cox   digi_acceleport: ...
367
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
6aad04f21   Jiri Slaby   TTY: add tty_port...
368
  	tty_port_tty_wakeup(&port->port);
c6d61269f   Alan Cox   digi_acceleport: ...
369
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
  /*
41ad427da   Alan Cox   digi_acceleport: ...
372
373
374
375
376
377
378
379
380
   *  Digi Write OOB Command
   *
   *  Write commands on the out of band port.  Commands are 4
   *  bytes each, multiple commands can be sent at once, and
   *  no command will be split across USB packets.  Returns 0
   *  if successful, -EINTR if interrupted while sleeping and
   *  the interruptible flag is true, or a negative error
   *  returned by usb_submit_urb.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381

c6d61269f   Alan Cox   digi_acceleport: ...
382
383
  static int digi_write_oob_command(struct usb_serial_port *port,
  	unsigned char *buf, int count, int interruptible)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
  	int ret = 0;
  	int len;
  	struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
  	struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
  	unsigned long flags = 0;
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
390
391
392
393
  	dev_dbg(&port->dev,
  		"digi_write_oob_command: TOP: port=%d, count=%d
  ",
  		oob_priv->dp_port_num, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394

c6d61269f   Alan Cox   digi_acceleport: ...
395
  	spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
41ad427da   Alan Cox   digi_acceleport: ...
396
  	while (count > 0) {
50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
397
  		while (oob_priv->dp_write_urb_in_use) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
399
  			cond_wait_interruptible_timeout_irqrestore(
  				&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
c6d61269f   Alan Cox   digi_acceleport: ...
400
401
402
403
  				&oob_priv->dp_port_lock, flags);
  			if (interruptible && signal_pending(current))
  				return -EINTR;
  			spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
  		}
  
  		/* len must be a multiple of 4, so commands are not split */
c6d61269f   Alan Cox   digi_acceleport: ...
407
408
  		len = min(count, oob_port->bulk_out_size);
  		if (len > 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  			len &= ~3;
c6d61269f   Alan Cox   digi_acceleport: ...
410
  		memcpy(oob_port->write_urb->transfer_buffer, buf, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
  		oob_port->write_urb->transfer_buffer_length = len;
41ad427da   Alan Cox   digi_acceleport: ...
412
413
  		ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
  		if (ret == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
  			oob_priv->dp_write_urb_in_use = 1;
  			count -= len;
  			buf += len;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
419
420
  	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
  	if (ret)
194343d93   Greg Kroah-Hartman   USB: remove use o...
421
422
423
  		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d
  ",
  			__func__, ret);
c6d61269f   Alan Cox   digi_acceleport: ...
424
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
429
  
  }
  
  
  /*
41ad427da   Alan Cox   digi_acceleport: ...
430
431
432
433
434
435
436
437
438
439
   *  Digi Write In Band Command
   *
   *  Write commands on the given port.  Commands are 4
   *  bytes each, multiple commands can be sent at once, and
   *  no command will be split across USB packets.  If timeout
   *  is non-zero, write in band command will return after
   *  waiting unsuccessfully for the URB status to clear for
   *  timeout ticks.  Returns 0 if successful, or a negative
   *  error returned by digi_write.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440

c6d61269f   Alan Cox   digi_acceleport: ...
441
442
  static int digi_write_inb_command(struct usb_serial_port *port,
  	unsigned char *buf, int count, unsigned long timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
448
  	int ret = 0;
  	int len;
  	struct digi_port *priv = usb_get_serial_port_data(port);
  	unsigned char *data = port->write_urb->transfer_buffer;
  	unsigned long flags = 0;
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
449
450
  	dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
451
  		priv->dp_port_num, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452

c6d61269f   Alan Cox   digi_acceleport: ...
453
  	if (timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
  		timeout += jiffies;
  	else
  		timeout = ULONG_MAX;
c6d61269f   Alan Cox   digi_acceleport: ...
457
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
41ad427da   Alan Cox   digi_acceleport: ...
458
  	while (count > 0 && ret == 0) {
50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
459
460
  		while (priv->dp_write_urb_in_use &&
  		       time_before(jiffies, timeout)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
  			cond_wait_interruptible_timeout_irqrestore(
  				&port->write_wait, DIGI_RETRY_TIMEOUT,
c6d61269f   Alan Cox   digi_acceleport: ...
463
464
465
466
  				&priv->dp_port_lock, flags);
  			if (signal_pending(current))
  				return -EINTR;
  			spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
  		}
  
  		/* len must be a multiple of 4 and small enough to */
  		/* guarantee the write will send buffered data first, */
  		/* so commands are in order with data and not split */
c6d61269f   Alan Cox   digi_acceleport: ...
472
473
  		len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
  		if (len > 4)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
  			len &= ~3;
  
  		/* write any buffered data first */
c6d61269f   Alan Cox   digi_acceleport: ...
477
  		if (priv->dp_out_buf_len > 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
  			data[0] = DIGI_CMD_SEND_DATA;
  			data[1] = priv->dp_out_buf_len;
c6d61269f   Alan Cox   digi_acceleport: ...
480
481
482
  			memcpy(data + 2, priv->dp_out_buf,
  				priv->dp_out_buf_len);
  			memcpy(data + 2 + priv->dp_out_buf_len, buf, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  			port->write_urb->transfer_buffer_length
c6d61269f   Alan Cox   digi_acceleport: ...
484
  				= priv->dp_out_buf_len + 2 + len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
  		} else {
c6d61269f   Alan Cox   digi_acceleport: ...
486
  			memcpy(data, buf, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
  			port->write_urb->transfer_buffer_length = len;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489

41ad427da   Alan Cox   digi_acceleport: ...
490
491
  		ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
  		if (ret == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
495
496
497
498
  			priv->dp_write_urb_in_use = 1;
  			priv->dp_out_buf_len = 0;
  			count -= len;
  			buf += len;
  		}
  
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
499
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500

c6d61269f   Alan Cox   digi_acceleport: ...
501
  	if (ret)
194343d93   Greg Kroah-Hartman   USB: remove use o...
502
503
504
  		dev_err(&port->dev,
  			"%s: usb_submit_urb failed, ret=%d, port=%d
  ",
441b62c1e   Harvey Harrison   USB: replace rema...
505
  			__func__, ret, priv->dp_port_num);
c6d61269f   Alan Cox   digi_acceleport: ...
506
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
510
  }
  
  
  /*
41ad427da   Alan Cox   digi_acceleport: ...
511
512
513
514
515
516
517
518
   *  Digi Set Modem Signals
   *
   *  Sets or clears DTR and RTS on the port, according to the
   *  modem_signals argument.  Use TIOCM_DTR and TIOCM_RTS flags
   *  for the modem_signals argument.  Returns 0 if successful,
   *  -EINTR if interrupted while sleeping, or a non-zero error
   *  returned by usb_submit_urb.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519

c6d61269f   Alan Cox   digi_acceleport: ...
520
521
  static int digi_set_modem_signals(struct usb_serial_port *port,
  	unsigned int modem_signals, int interruptible)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
523
524
525
  {
  
  	int ret;
  	struct digi_port *port_priv = usb_get_serial_port_data(port);
41ad427da   Alan Cox   digi_acceleport: ...
526
  	struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
  	struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
  	unsigned char *data = oob_port->write_urb->transfer_buffer;
  	unsigned long flags = 0;
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
530
531
532
  	dev_dbg(&port->dev,
  		"digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
533
  		port_priv->dp_port_num, modem_signals);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534

c6d61269f   Alan Cox   digi_acceleport: ...
535
536
  	spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
  	spin_lock(&port_priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537

50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
538
  	while (oob_priv->dp_write_urb_in_use) {
c6d61269f   Alan Cox   digi_acceleport: ...
539
  		spin_unlock(&port_priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
  		cond_wait_interruptible_timeout_irqrestore(
  			&oob_port->write_wait, DIGI_RETRY_TIMEOUT,
c6d61269f   Alan Cox   digi_acceleport: ...
542
543
544
545
546
  			&oob_priv->dp_port_lock, flags);
  		if (interruptible && signal_pending(current))
  			return -EINTR;
  		spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
  		spin_lock(&port_priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
  	data[0] = DIGI_CMD_SET_DTR_SIGNAL;
  	data[1] = port_priv->dp_port_num;
41ad427da   Alan Cox   digi_acceleport: ...
550
551
  	data[2] = (modem_signals & TIOCM_DTR) ?
  					DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  	data[3] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  	data[4] = DIGI_CMD_SET_RTS_SIGNAL;
  	data[5] = port_priv->dp_port_num;
41ad427da   Alan Cox   digi_acceleport: ...
555
556
  	data[6] = (modem_signals & TIOCM_RTS) ?
  					DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
557
558
559
  	data[7] = 0;
  
  	oob_port->write_urb->transfer_buffer_length = 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

41ad427da   Alan Cox   digi_acceleport: ...
561
562
  	ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC);
  	if (ret == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
565
566
567
  		oob_priv->dp_write_urb_in_use = 1;
  		port_priv->dp_modem_signals =
  			(port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS))
  			| (modem_signals&(TIOCM_DTR|TIOCM_RTS));
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
568
569
570
  	spin_unlock(&port_priv->dp_port_lock);
  	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);
  	if (ret)
194343d93   Greg Kroah-Hartman   USB: remove use o...
571
572
573
  		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d
  ",
  			__func__, ret);
c6d61269f   Alan Cox   digi_acceleport: ...
574
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
  /*
41ad427da   Alan Cox   digi_acceleport: ...
577
578
579
580
581
582
583
584
585
586
   *  Digi Transmit Idle
   *
   *  Digi transmit idle waits, up to timeout ticks, for the transmitter
   *  to go idle.  It returns 0 if successful or a negative error.
   *
   *  There are race conditions here if more than one process is calling
   *  digi_transmit_idle on the same port at the same time.  However, this
   *  is only called from close, and only one process can be in close on a
   *  port at a time, so its ok.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587

c6d61269f   Alan Cox   digi_acceleport: ...
588
589
  static int digi_transmit_idle(struct usb_serial_port *port,
  	unsigned long timeout)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
592
593
594
  	int ret;
  	unsigned char buf[2];
  	struct digi_port *priv = usb_get_serial_port_data(port);
  	unsigned long flags = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
595
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
  	priv->dp_transmit_idle = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
597
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
  
  	buf[0] = DIGI_CMD_TRANSMIT_IDLE;
  	buf[1] = 0;
  
  	timeout += jiffies;
41ad427da   Alan Cox   digi_acceleport: ...
603
604
  	ret = digi_write_inb_command(port, buf, 2, timeout - jiffies);
  	if (ret != 0)
c6d61269f   Alan Cox   digi_acceleport: ...
605
  		return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606

c6d61269f   Alan Cox   digi_acceleport: ...
607
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608

41ad427da   Alan Cox   digi_acceleport: ...
609
  	while (time_before(jiffies, timeout) && !priv->dp_transmit_idle) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
  		cond_wait_interruptible_timeout_irqrestore(
  			&priv->dp_transmit_idle_wait, DIGI_RETRY_TIMEOUT,
c6d61269f   Alan Cox   digi_acceleport: ...
612
613
614
615
  			&priv->dp_port_lock, flags);
  		if (signal_pending(current))
  			return -EINTR;
  		spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
  	priv->dp_transmit_idle = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
618
619
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
  
  }
95da310e6   Alan Cox   usb_serial: API a...
622
  static void digi_rx_throttle(struct tty_struct *tty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
  	unsigned long flags;
95da310e6   Alan Cox   usb_serial: API a...
625
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
  	struct digi_port *priv = usb_get_serial_port_data(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
  	/* stop receiving characters by not resubmitting the read urb */
c6d61269f   Alan Cox   digi_acceleport: ...
628
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
  	priv->dp_throttled = 1;
  	priv->dp_throttle_restart = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
631
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
  }
95da310e6   Alan Cox   usb_serial: API a...
633
  static void digi_rx_unthrottle(struct tty_struct *tty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  	unsigned long flags;
95da310e6   Alan Cox   usb_serial: API a...
637
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
  	struct digi_port *priv = usb_get_serial_port_data(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639

c6d61269f   Alan Cox   digi_acceleport: ...
640
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
  	/* restart read chain */
5833041f1   Johan Hovold   USB: serial: remo...
643
  	if (priv->dp_throttle_restart)
c6d61269f   Alan Cox   digi_acceleport: ...
644
  		ret = usb_submit_urb(port->read_urb, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645

ba6b702f8   Johan Hovold   USB: digi_accelep...
646
647
648
  	/* turn throttle off */
  	priv->dp_throttled = 0;
  	priv->dp_throttle_restart = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
649
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650

c6d61269f   Alan Cox   digi_acceleport: ...
651
  	if (ret)
194343d93   Greg Kroah-Hartman   USB: remove use o...
652
653
654
  		dev_err(&port->dev,
  			"%s: usb_submit_urb failed, ret=%d, port=%d
  ",
441b62c1e   Harvey Harrison   USB: replace rema...
655
  			__func__, ret, priv->dp_port_num);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
  }
41ad427da   Alan Cox   digi_acceleport: ...
657
  static void digi_set_termios(struct tty_struct *tty,
95da310e6   Alan Cox   usb_serial: API a...
658
  		struct usb_serial_port *port, struct ktermios *old_termios)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  	struct digi_port *priv = usb_get_serial_port_data(port);
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
661
  	struct device *dev = &port->dev;
adc8d746c   Alan Cox   tty: move the ter...
662
663
  	unsigned int iflag = tty->termios.c_iflag;
  	unsigned int cflag = tty->termios.c_cflag;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
666
667
  	unsigned int old_iflag = old_termios->c_iflag;
  	unsigned int old_cflag = old_termios->c_cflag;
  	unsigned char buf[32];
  	unsigned int modem_signals;
41ad427da   Alan Cox   digi_acceleport: ...
668
  	int arg, ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
  	int i = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
670
  	speed_t baud;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671

f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
672
673
674
675
  	dev_dbg(dev,
  		"digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x
  ",
  		priv->dp_port_num, iflag, old_iflag, cflag, old_cflag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
  
  	/* set baud rate */
41ad427da   Alan Cox   digi_acceleport: ...
678
679
  	baud = tty_get_baud_rate(tty);
  	if (baud != tty_termios_baud_rate(old_termios)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
682
  		arg = -1;
  
  		/* reassert DTR and (maybe) RTS on transition from B0 */
9db276f8f   Peter Hurley   tty: Use termios ...
683
  		if ((old_cflag & CBAUD) == B0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
  			/* don't set RTS if using hardware flow control */
  			/* and throttling input */
  			modem_signals = TIOCM_DTR;
97ef38b82   Peter Hurley   tty: Replace TTY_...
687
  			if (!C_CRTSCTS(tty) || !tty_throttled(tty))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  				modem_signals |= TIOCM_RTS;
c6d61269f   Alan Cox   digi_acceleport: ...
689
  			digi_set_modem_signals(port, modem_signals, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
  		}
c6d61269f   Alan Cox   digi_acceleport: ...
691
  		switch (baud) {
41ad427da   Alan Cox   digi_acceleport: ...
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
  		/* drop DTR and RTS on transition to B0 */
  		case 0: digi_set_modem_signals(port, 0, 1); break;
  		case 50: arg = DIGI_BAUD_50; break;
  		case 75: arg = DIGI_BAUD_75; break;
  		case 110: arg = DIGI_BAUD_110; break;
  		case 150: arg = DIGI_BAUD_150; break;
  		case 200: arg = DIGI_BAUD_200; break;
  		case 300: arg = DIGI_BAUD_300; break;
  		case 600: arg = DIGI_BAUD_600; break;
  		case 1200: arg = DIGI_BAUD_1200; break;
  		case 1800: arg = DIGI_BAUD_1800; break;
  		case 2400: arg = DIGI_BAUD_2400; break;
  		case 4800: arg = DIGI_BAUD_4800; break;
  		case 9600: arg = DIGI_BAUD_9600; break;
  		case 19200: arg = DIGI_BAUD_19200; break;
  		case 38400: arg = DIGI_BAUD_38400; break;
  		case 57600: arg = DIGI_BAUD_57600; break;
  		case 115200: arg = DIGI_BAUD_115200; break;
  		case 230400: arg = DIGI_BAUD_230400; break;
  		case 460800: arg = DIGI_BAUD_460800; break;
  		default:
  			arg = DIGI_BAUD_9600;
  			baud = 9600;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  		}
c6d61269f   Alan Cox   digi_acceleport: ...
717
  		if (arg != -1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
721
722
  			buf[i++] = DIGI_CMD_SET_BAUD_RATE;
  			buf[i++] = priv->dp_port_num;
  			buf[i++] = arg;
  			buf[i++] = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  	/* set parity */
adc8d746c   Alan Cox   tty: move the ter...
725
  	tty->termios.c_cflag &= ~CMSPAR;
7fa36a994   Alan Cox   USB: digi_accelep...
726

c6d61269f   Alan Cox   digi_acceleport: ...
727
728
729
  	if ((cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD))) {
  		if (cflag&PARENB) {
  			if (cflag&PARODD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
735
  				arg = DIGI_PARITY_ODD;
  			else
  				arg = DIGI_PARITY_EVEN;
  		} else {
  			arg = DIGI_PARITY_NONE;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
  		buf[i++] = DIGI_CMD_SET_PARITY;
  		buf[i++] = priv->dp_port_num;
  		buf[i++] = arg;
  		buf[i++] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
  	/* set word size */
c6d61269f   Alan Cox   digi_acceleport: ...
742
  	if ((cflag&CSIZE) != (old_cflag&CSIZE)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
  		arg = -1;
c6d61269f   Alan Cox   digi_acceleport: ...
744
  		switch (cflag&CSIZE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
749
  		case CS5: arg = DIGI_WORD_SIZE_5; break;
  		case CS6: arg = DIGI_WORD_SIZE_6; break;
  		case CS7: arg = DIGI_WORD_SIZE_7; break;
  		case CS8: arg = DIGI_WORD_SIZE_8; break;
  		default:
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
750
751
752
  			dev_dbg(dev,
  				"digi_set_termios: can't handle word size %d
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
753
  				(cflag&CSIZE));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
  			break;
  		}
c6d61269f   Alan Cox   digi_acceleport: ...
756
  		if (arg != -1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
759
760
761
762
763
764
765
  			buf[i++] = DIGI_CMD_SET_WORD_SIZE;
  			buf[i++] = priv->dp_port_num;
  			buf[i++] = arg;
  			buf[i++] = 0;
  		}
  
  	}
  
  	/* set stop bits */
c6d61269f   Alan Cox   digi_acceleport: ...
766
  	if ((cflag&CSTOPB) != (old_cflag&CSTOPB)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767

c6d61269f   Alan Cox   digi_acceleport: ...
768
  		if ((cflag&CSTOPB))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
776
777
778
779
780
  			arg = DIGI_STOP_BITS_2;
  		else
  			arg = DIGI_STOP_BITS_1;
  
  		buf[i++] = DIGI_CMD_SET_STOP_BITS;
  		buf[i++] = priv->dp_port_num;
  		buf[i++] = arg;
  		buf[i++] = 0;
  
  	}
  
  	/* set input flow control */
c6d61269f   Alan Cox   digi_acceleport: ...
781
782
  	if ((iflag&IXOFF) != (old_iflag&IXOFF)
  	    || (cflag&CRTSCTS) != (old_cflag&CRTSCTS)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
  		arg = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
784
  		if (iflag&IXOFF)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
  			arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
  		else
  			arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF;
c6d61269f   Alan Cox   digi_acceleport: ...
788
  		if (cflag&CRTSCTS) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
790
791
792
793
794
795
796
797
798
799
800
  			arg |= DIGI_INPUT_FLOW_CONTROL_RTS;
  
  			/* On USB-4 it is necessary to assert RTS prior */
  			/* to selecting RTS input flow control.  */
  			buf[i++] = DIGI_CMD_SET_RTS_SIGNAL;
  			buf[i++] = priv->dp_port_num;
  			buf[i++] = DIGI_RTS_ACTIVE;
  			buf[i++] = 0;
  
  		} else {
  			arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
803
804
  		buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
  		buf[i++] = priv->dp_port_num;
  		buf[i++] = arg;
  		buf[i++] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
  	}
  
  	/* set output flow control */
7fa36a994   Alan Cox   USB: digi_accelep...
808
809
  	if ((iflag & IXON) != (old_iflag & IXON)
  	    || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  		arg = 0;
7fa36a994   Alan Cox   USB: digi_accelep...
811
  		if (iflag & IXON)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
  			arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
  		else
  			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF;
7fa36a994   Alan Cox   USB: digi_accelep...
815
  		if (cflag & CRTSCTS) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
818
  			arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS;
  		} else {
  			arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
821
822
823
824
  		}
  
  		buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
  		buf[i++] = priv->dp_port_num;
  		buf[i++] = arg;
  		buf[i++] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
826
827
  	}
  
  	/* set receive enable/disable */
7fa36a994   Alan Cox   USB: digi_accelep...
828
829
  	if ((cflag & CREAD) != (old_cflag & CREAD)) {
  		if (cflag & CREAD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
832
833
834
835
836
837
  			arg = DIGI_ENABLE;
  		else
  			arg = DIGI_DISABLE;
  
  		buf[i++] = DIGI_CMD_RECEIVE_ENABLE;
  		buf[i++] = priv->dp_port_num;
  		buf[i++] = arg;
  		buf[i++] = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
  	}
41ad427da   Alan Cox   digi_acceleport: ...
839
840
  	ret = digi_write_oob_command(port, buf, i, 1);
  	if (ret != 0)
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
841
842
  		dev_dbg(dev, "digi_set_termios: write oob failed, ret=%d
  ", ret);
7fa36a994   Alan Cox   USB: digi_accelep...
843
  	tty_encode_baud_rate(tty, baud, baud);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
  }
95da310e6   Alan Cox   usb_serial: API a...
845
  static void digi_break_ctl(struct tty_struct *tty, int break_state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
  {
95da310e6   Alan Cox   usb_serial: API a...
847
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  	unsigned char buf[4];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
850
851
852
  	buf[0] = DIGI_CMD_BREAK_CONTROL;
  	buf[1] = 2;				/* length */
  	buf[2] = break_state ? 1 : 0;
  	buf[3] = 0;				/* pad */
c6d61269f   Alan Cox   digi_acceleport: ...
853
  	digi_write_inb_command(port, buf, 4, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
  }
60b33c133   Alan Cox   tiocmget: kill of...
855
  static int digi_tiocmget(struct tty_struct *tty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  {
95da310e6   Alan Cox   usb_serial: API a...
857
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
860
  	struct digi_port *priv = usb_get_serial_port_data(port);
  	unsigned int val;
  	unsigned long flags;
c6d61269f   Alan Cox   digi_acceleport: ...
861
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  	val = priv->dp_modem_signals;
c6d61269f   Alan Cox   digi_acceleport: ...
863
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
  	return val;
  }
20b9d1771   Alan Cox   tiocmset: kill th...
866
867
  static int digi_tiocmset(struct tty_struct *tty,
  					unsigned int set, unsigned int clear)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
  {
95da310e6   Alan Cox   usb_serial: API a...
869
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
870
871
872
  	struct digi_port *priv = usb_get_serial_port_data(port);
  	unsigned int val;
  	unsigned long flags;
c6d61269f   Alan Cox   digi_acceleport: ...
873
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
  	val = (priv->dp_modem_signals & ~clear) | set;
c6d61269f   Alan Cox   digi_acceleport: ...
875
876
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
  	return digi_set_modem_signals(port, val, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
  }
95da310e6   Alan Cox   usb_serial: API a...
878
879
  static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
  					const unsigned char *buf, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
  {
41ad427da   Alan Cox   digi_acceleport: ...
881
  	int ret, data_len, new_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
884
  	struct digi_port *priv = usb_get_serial_port_data(port);
  	unsigned char *data = port->write_urb->transfer_buffer;
  	unsigned long flags = 0;
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
885
886
887
  	dev_dbg(&port->dev,
  		"digi_write: TOP: port=%d, count=%d, in_interrupt=%ld
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
888
  		priv->dp_port_num, count, in_interrupt());
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
  
  	/* copy user data (which can sleep) before getting spin lock */
c6d61269f   Alan Cox   digi_acceleport: ...
891
892
  	count = min(count, port->bulk_out_size-2);
  	count = min(64, count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
  
  	/* be sure only one write proceeds at a time */
  	/* there are races on the port private buffer */
c6d61269f   Alan Cox   digi_acceleport: ...
896
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
  
  	/* wait for urb status clear to submit another urb */
50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
899
  	if (priv->dp_write_urb_in_use) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
  		/* buffer data if count is 1 (probably put_char) if possible */
c6d61269f   Alan Cox   digi_acceleport: ...
901
  		if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
905
906
  			priv->dp_out_buf[priv->dp_out_buf_len++] = *buf;
  			new_len = 1;
  		} else {
  			new_len = 0;
  		}
c6d61269f   Alan Cox   digi_acceleport: ...
907
908
  		spin_unlock_irqrestore(&priv->dp_port_lock, flags);
  		return new_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
910
911
912
913
914
  	}
  
  	/* allow space for any buffered data and for new data, up to */
  	/* transfer buffer size - 2 (for command and length bytes) */
  	new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len);
  	data_len = new_len + priv->dp_out_buf_len;
c6d61269f   Alan Cox   digi_acceleport: ...
915
916
917
  	if (data_len == 0) {
  		spin_unlock_irqrestore(&priv->dp_port_lock, flags);
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
  	}
  
  	port->write_urb->transfer_buffer_length = data_len+2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
923
924
925
  
  	*data++ = DIGI_CMD_SEND_DATA;
  	*data++ = data_len;
  
  	/* copy in buffered data first */
c6d61269f   Alan Cox   digi_acceleport: ...
926
  	memcpy(data, priv->dp_out_buf, priv->dp_out_buf_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
928
929
  	data += priv->dp_out_buf_len;
  
  	/* copy in new data */
c6d61269f   Alan Cox   digi_acceleport: ...
930
  	memcpy(data, buf, new_len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931

41ad427da   Alan Cox   digi_acceleport: ...
932
933
  	ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
  	if (ret == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
936
937
938
939
  		priv->dp_write_urb_in_use = 1;
  		ret = new_len;
  		priv->dp_out_buf_len = 0;
  	}
  
  	/* return length of new data written, or error */
c6d61269f   Alan Cox   digi_acceleport: ...
940
941
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
  	if (ret < 0)
22a416c4e   Johan Hovold   USB: serial: use ...
942
  		dev_err_console(port,
194343d93   Greg Kroah-Hartman   USB: remove use o...
943
944
  			"%s: usb_submit_urb failed, ret=%d, port=%d
  ",
441b62c1e   Harvey Harrison   USB: replace rema...
945
  			__func__, ret, priv->dp_port_num);
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
946
947
  	dev_dbg(&port->dev, "digi_write: returning %d
  ", ret);
c6d61269f   Alan Cox   digi_acceleport: ...
948
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949

41ad427da   Alan Cox   digi_acceleport: ...
950
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951

c6d61269f   Alan Cox   digi_acceleport: ...
952
  static void digi_write_bulk_callback(struct urb *urb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
  {
cdc977922   Ming Lei   USB: remove unnec...
954
  	struct usb_serial_port *port = urb->context;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
956
957
958
  	struct usb_serial *serial;
  	struct digi_port *priv;
  	struct digi_serial *serial_priv;
  	int ret = 0;
85d751071   Greg Kroah-Hartman   USB: serial: digi...
959
  	int status = urb->status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
  	/* port and serial sanity check */
41ad427da   Alan Cox   digi_acceleport: ...
962
  	if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) {
109f34e71   Julia Lawall   USB: serial: digi...
963
964
  		pr_err("%s: port or port->private is NULL, status=%d
  ",
194343d93   Greg Kroah-Hartman   USB: remove use o...
965
  			__func__, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
966
967
968
  		return;
  	}
  	serial = port->serial;
41ad427da   Alan Cox   digi_acceleport: ...
969
  	if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) {
194343d93   Greg Kroah-Hartman   USB: remove use o...
970
971
972
973
  		dev_err(&port->dev,
  			"%s: serial or serial->private is NULL, status=%d
  ",
  			__func__, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
976
977
  		return;
  	}
  
  	/* handle oob callback */
c6d61269f   Alan Cox   digi_acceleport: ...
978
  	if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
979
980
  		dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback
  ");
c6d61269f   Alan Cox   digi_acceleport: ...
981
  		spin_lock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
  		priv->dp_write_urb_in_use = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
983
984
  		wake_up_interruptible(&port->write_wait);
  		spin_unlock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
985
986
  		return;
  	}
1f87158e4   Alan Stern   USB: remove refer...
987
  	/* try to send any buffered data on this port */
c6d61269f   Alan Cox   digi_acceleport: ...
988
  	spin_lock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  	priv->dp_write_urb_in_use = 0;
1f87158e4   Alan Stern   USB: remove refer...
990
  	if (priv->dp_out_buf_len > 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
992
  		*((unsigned char *)(port->write_urb->transfer_buffer))
  			= (unsigned char)DIGI_CMD_SEND_DATA;
41ad427da   Alan Cox   digi_acceleport: ...
993
  		*((unsigned char *)(port->write_urb->transfer_buffer) + 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
  			= (unsigned char)priv->dp_out_buf_len;
41ad427da   Alan Cox   digi_acceleport: ...
995
996
  		port->write_urb->transfer_buffer_length =
  						priv->dp_out_buf_len + 2;
41ad427da   Alan Cox   digi_acceleport: ...
997
  		memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf,
c6d61269f   Alan Cox   digi_acceleport: ...
998
  			priv->dp_out_buf_len);
41ad427da   Alan Cox   digi_acceleport: ...
999
1000
  		ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
  		if (ret == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
  			priv->dp_write_urb_in_use = 1;
  			priv->dp_out_buf_len = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  	/* wake up processes sleeping on writes immediately */
6aad04f21   Jiri Slaby   TTY: add tty_port...
1006
  	tty_port_tty_wakeup(&port->port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
1008
1009
  	/* also queue up a wakeup at scheduler time, in case we */
  	/* lost the race in write_chan(). */
  	schedule_work(&priv->dp_wakeup_work);
c6d61269f   Alan Cox   digi_acceleport: ...
1010
  	spin_unlock(&priv->dp_port_lock);
1f87158e4   Alan Stern   USB: remove refer...
1011
  	if (ret && ret != -EPERM)
22a416c4e   Johan Hovold   USB: serial: use ...
1012
  		dev_err_console(port,
194343d93   Greg Kroah-Hartman   USB: remove use o...
1013
1014
  			"%s: usb_submit_urb failed, ret=%d, port=%d
  ",
441b62c1e   Harvey Harrison   USB: replace rema...
1015
  			__func__, ret, priv->dp_port_num);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
  }
95da310e6   Alan Cox   usb_serial: API a...
1017
  static int digi_write_room(struct tty_struct *tty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
  {
95da310e6   Alan Cox   usb_serial: API a...
1019
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
  	struct digi_port *priv = usb_get_serial_port_data(port);
95da310e6   Alan Cox   usb_serial: API a...
1021
  	int room;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
  	unsigned long flags = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
1023
  	spin_lock_irqsave(&priv->dp_port_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1024

50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
1025
  	if (priv->dp_write_urb_in_use)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
1027
1028
  		room = 0;
  	else
  		room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
c6d61269f   Alan Cox   digi_acceleport: ...
1029
  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1030
1031
  	dev_dbg(&port->dev, "digi_write_room: port=%d, room=%d
  ", priv->dp_port_num, room);
c6d61269f   Alan Cox   digi_acceleport: ...
1032
  	return room;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1033
1034
  
  }
95da310e6   Alan Cox   usb_serial: API a...
1035
  static int digi_chars_in_buffer(struct tty_struct *tty)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
  {
95da310e6   Alan Cox   usb_serial: API a...
1037
  	struct usb_serial_port *port = tty->driver_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
  	struct digi_port *priv = usb_get_serial_port_data(port);
50de36f7a   Greg Kroah-Hartman   USB: serial: fix ...
1039
  	if (priv->dp_write_urb_in_use) {
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1040
1041
  		dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
1042
1043
1044
  			priv->dp_port_num, port->bulk_out_size - 2);
  		/* return(port->bulk_out_size - 2); */
  		return 256;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
  	} else {
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1046
1047
  		dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
1048
1049
  			priv->dp_port_num, priv->dp_out_buf_len);
  		return priv->dp_out_buf_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1050
1051
1052
  	}
  
  }
335f8514f   Alan Cox   tty: Bring the us...
1053
1054
1055
1056
1057
  static void digi_dtr_rts(struct usb_serial_port *port, int on)
  {
  	/* Adjust DTR and RTS */
  	digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1);
  }
a509a7e47   Alan Cox   tty: USB does not...
1058
  static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
  	int ret;
  	unsigned char buf[32];
  	struct digi_port *priv = usb_get_serial_port_data(port);
606d099cd   Alan Cox   [PATCH] tty: swit...
1063
  	struct ktermios not_termios;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
  	/* be sure the device is started up */
c6d61269f   Alan Cox   digi_acceleport: ...
1066
1067
  	if (digi_startup_device(port->serial) != 0)
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
  	/* read modem signals automatically whenever they change */
  	buf[0] = DIGI_CMD_READ_INPUT_SIGNALS;
  	buf[1] = priv->dp_port_num;
  	buf[2] = DIGI_ENABLE;
  	buf[3] = 0;
  
  	/* flush fifos */
  	buf[4] = DIGI_CMD_IFLUSH_FIFO;
  	buf[5] = priv->dp_port_num;
  	buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
  	buf[7] = 0;
41ad427da   Alan Cox   digi_acceleport: ...
1080
1081
  	ret = digi_write_oob_command(port, buf, 8, 1);
  	if (ret != 0)
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1082
1083
  		dev_dbg(&port->dev, "digi_open: write oob failed, ret=%d
  ", ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1084
1085
  
  	/* set termios settings */
95da310e6   Alan Cox   usb_serial: API a...
1086
  	if (tty) {
adc8d746c   Alan Cox   tty: move the ter...
1087
1088
  		not_termios.c_cflag = ~tty->termios.c_cflag;
  		not_termios.c_iflag = ~tty->termios.c_iflag;
95da310e6   Alan Cox   usb_serial: API a...
1089
1090
  		digi_set_termios(tty, port, &not_termios);
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1091
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1092
  }
335f8514f   Alan Cox   tty: Bring the us...
1093
  static void digi_close(struct usb_serial_port *port)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
1095
1096
1097
  {
  	DEFINE_WAIT(wait);
  	int ret;
  	unsigned char buf[32];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
  	struct digi_port *priv = usb_get_serial_port_data(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099

0915f490d   Oliver Neukum   USB: last abuses ...
1100
  	mutex_lock(&port->serial->disc_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1101
  	/* if disconnected, just clear flags */
0915f490d   Oliver Neukum   USB: last abuses ...
1102
  	if (port->serial->disconnected)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
  		goto exit;
28e679ae6   Johan Hovold   USB: digi_accelep...
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
  	/* FIXME: Transmit idle belongs in the wait_unti_sent path */
  	digi_transmit_idle(port, DIGI_CLOSE_TIMEOUT);
  
  	/* disable input flow control */
  	buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL;
  	buf[1] = priv->dp_port_num;
  	buf[2] = DIGI_DISABLE;
  	buf[3] = 0;
  
  	/* disable output flow control */
  	buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL;
  	buf[5] = priv->dp_port_num;
  	buf[6] = DIGI_DISABLE;
  	buf[7] = 0;
  
  	/* disable reading modem signals automatically */
  	buf[8] = DIGI_CMD_READ_INPUT_SIGNALS;
  	buf[9] = priv->dp_port_num;
  	buf[10] = DIGI_DISABLE;
  	buf[11] = 0;
  
  	/* disable receive */
  	buf[12] = DIGI_CMD_RECEIVE_ENABLE;
  	buf[13] = priv->dp_port_num;
  	buf[14] = DIGI_DISABLE;
  	buf[15] = 0;
  
  	/* flush fifos */
  	buf[16] = DIGI_CMD_IFLUSH_FIFO;
  	buf[17] = priv->dp_port_num;
  	buf[18] = DIGI_FLUSH_TX | DIGI_FLUSH_RX;
  	buf[19] = 0;
  
  	ret = digi_write_oob_command(port, buf, 20, 0);
  	if (ret != 0)
  		dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d
  ",
  									ret);
  	/* wait for final commands on oob port to complete */
  	prepare_to_wait(&priv->dp_flush_wait, &wait,
  			TASK_INTERRUPTIBLE);
  	schedule_timeout(DIGI_CLOSE_TIMEOUT);
  	finish_wait(&priv->dp_flush_wait, &wait);
  
  	/* shutdown any outstanding bulk writes */
  	usb_kill_urb(port->write_urb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
  exit:
0915f490d   Oliver Neukum   USB: last abuses ...
1151
  	spin_lock_irq(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1152
  	priv->dp_write_urb_in_use = 0;
c6d61269f   Alan Cox   digi_acceleport: ...
1153
  	wake_up_interruptible(&priv->dp_close_wait);
0915f490d   Oliver Neukum   USB: last abuses ...
1154
1155
  	spin_unlock_irq(&priv->dp_port_lock);
  	mutex_unlock(&port->serial->disc_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1156
1157
1158
1159
  }
  
  
  /*
41ad427da   Alan Cox   digi_acceleport: ...
1160
1161
1162
1163
1164
   *  Digi Startup Device
   *
   *  Starts reads on all ports.  Must be called AFTER startup, with
   *  urbs initialized.  Returns 0 if successful, non-zero error otherwise.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165

c6d61269f   Alan Cox   digi_acceleport: ...
1166
  static int digi_startup_device(struct usb_serial *serial)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1167
  {
41ad427da   Alan Cox   digi_acceleport: ...
1168
  	int i, ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1169
1170
  	struct digi_serial *serial_priv = usb_get_serial_data(serial);
  	struct usb_serial_port *port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1171
  	/* be sure this happens exactly once */
c6d61269f   Alan Cox   digi_acceleport: ...
1172
1173
1174
1175
  	spin_lock(&serial_priv->ds_serial_lock);
  	if (serial_priv->ds_device_started) {
  		spin_unlock(&serial_priv->ds_serial_lock);
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176
1177
  	}
  	serial_priv->ds_device_started = 1;
c6d61269f   Alan Cox   digi_acceleport: ...
1178
  	spin_unlock(&serial_priv->ds_serial_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1179
1180
1181
  
  	/* start reading from each bulk in endpoint for the device */
  	/* set USB_DISABLE_SPD flag for write bulk urbs */
c6d61269f   Alan Cox   digi_acceleport: ...
1182
  	for (i = 0; i < serial->type->num_ports + 1; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1183
  		port = serial->port[i];
41ad427da   Alan Cox   digi_acceleport: ...
1184
1185
  		ret = usb_submit_urb(port->read_urb, GFP_KERNEL);
  		if (ret != 0) {
194343d93   Greg Kroah-Hartman   USB: remove use o...
1186
1187
1188
1189
  			dev_err(&port->dev,
  				"%s: usb_submit_urb failed, ret=%d, port=%d
  ",
  				__func__, ret, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190
1191
  			break;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1193
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
  }
fb44ff854   Johan Hovold   USB: digi_accelep...
1195
  static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1197
  	struct digi_port *priv;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198

fb44ff854   Johan Hovold   USB: digi_accelep...
1199
1200
1201
  	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  	if (!priv)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1202

fb44ff854   Johan Hovold   USB: digi_accelep...
1203
1204
  	spin_lock_init(&priv->dp_port_lock);
  	priv->dp_port_num = port_num;
fb44ff854   Johan Hovold   USB: digi_accelep...
1205
1206
1207
1208
1209
  	init_waitqueue_head(&priv->dp_transmit_idle_wait);
  	init_waitqueue_head(&priv->dp_flush_wait);
  	init_waitqueue_head(&priv->dp_close_wait);
  	INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
  	priv->dp_port = port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1210

fb44ff854   Johan Hovold   USB: digi_accelep...
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
  	init_waitqueue_head(&port->write_wait);
  
  	usb_set_serial_port_data(port, priv);
  
  	return 0;
  }
  
  static int digi_startup(struct usb_serial *serial)
  {
  	struct digi_serial *serial_priv;
  	int ret;
  
  	serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
  	if (!serial_priv)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1226

c6d61269f   Alan Cox   digi_acceleport: ...
1227
  	spin_lock_init(&serial_priv->ds_serial_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
1229
  	serial_priv->ds_oob_port_num = serial->type->num_ports;
  	serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
fb44ff854   Johan Hovold   USB: digi_accelep...
1230
1231
1232
1233
1234
1235
1236
  
  	ret = digi_port_init(serial_priv->ds_oob_port,
  						serial_priv->ds_oob_port_num);
  	if (ret) {
  		kfree(serial_priv);
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1237
  	usb_set_serial_data(serial, serial_priv);
c6d61269f   Alan Cox   digi_acceleport: ...
1238
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1239
  }
f9c99bb8b   Alan Stern   USB: usb-serial: ...
1240
  static void digi_disconnect(struct usb_serial *serial)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1241
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1242
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
1244
  
  	/* stop reads and writes on all ports */
c6d61269f   Alan Cox   digi_acceleport: ...
1245
  	for (i = 0; i < serial->type->num_ports + 1; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
1247
1248
  		usb_kill_urb(serial->port[i]->read_urb);
  		usb_kill_urb(serial->port[i]->write_urb);
  	}
f9c99bb8b   Alan Stern   USB: usb-serial: ...
1249
1250
1251
1252
1253
  }
  
  
  static void digi_release(struct usb_serial *serial)
  {
fb44ff854   Johan Hovold   USB: digi_accelep...
1254
1255
1256
1257
1258
1259
1260
  	struct digi_serial *serial_priv;
  	struct digi_port *priv;
  
  	serial_priv = usb_get_serial_data(serial);
  
  	priv = usb_get_serial_port_data(serial_priv->ds_oob_port);
  	kfree(priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1261

fb44ff854   Johan Hovold   USB: digi_accelep...
1262
  	kfree(serial_priv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1263
  }
fb44ff854   Johan Hovold   USB: digi_accelep...
1264
1265
  static int digi_port_probe(struct usb_serial_port *port)
  {
1143832ec   Greg Kroah-Hartman   USB: serial: port...
1266
  	return digi_port_init(port, port->port_number);
fb44ff854   Johan Hovold   USB: digi_accelep...
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
  }
  
  static int digi_port_remove(struct usb_serial_port *port)
  {
  	struct digi_port *priv;
  
  	priv = usb_get_serial_port_data(port);
  	kfree(priv);
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1278

c6d61269f   Alan Cox   digi_acceleport: ...
1279
  static void digi_read_bulk_callback(struct urb *urb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1280
  {
cdc977922   Ming Lei   USB: remove unnec...
1281
  	struct usb_serial_port *port = urb->context;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1282
1283
1284
  	struct digi_port *priv;
  	struct digi_serial *serial_priv;
  	int ret;
85d751071   Greg Kroah-Hartman   USB: serial: digi...
1285
  	int status = urb->status;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
  	/* port sanity check, do not resubmit if port is not valid */
194343d93   Greg Kroah-Hartman   USB: remove use o...
1288
1289
1290
1291
1292
1293
1294
  	if (port == NULL)
  		return;
  	priv = usb_get_serial_port_data(port);
  	if (priv == NULL) {
  		dev_err(&port->dev, "%s: port->private is NULL, status=%d
  ",
  			__func__, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1295
1296
  		return;
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1297
  	if (port->serial == NULL ||
41ad427da   Alan Cox   digi_acceleport: ...
1298
  		(serial_priv = usb_get_serial_data(port->serial)) == NULL) {
194343d93   Greg Kroah-Hartman   USB: remove use o...
1299
1300
1301
  		dev_err(&port->dev, "%s: serial is bad or serial->private "
  			"is NULL, status=%d
  ", __func__, status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302
1303
1304
1305
  		return;
  	}
  
  	/* do not resubmit urb if it has any status error */
85d751071   Greg Kroah-Hartman   USB: serial: digi...
1306
  	if (status) {
194343d93   Greg Kroah-Hartman   USB: remove use o...
1307
1308
1309
1310
  		dev_err(&port->dev,
  			"%s: nonzero read bulk status: status=%d, port=%d
  ",
  			__func__, status, priv->dp_port_num);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1311
1312
1313
1314
  		return;
  	}
  
  	/* handle oob or inb callback, do not resubmit if error */
c6d61269f   Alan Cox   digi_acceleport: ...
1315
1316
  	if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
  		if (digi_read_oob_callback(urb) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1317
1318
  			return;
  	} else {
c6d61269f   Alan Cox   digi_acceleport: ...
1319
  		if (digi_read_inb_callback(urb) != 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1320
1321
1322
1323
  			return;
  	}
  
  	/* continue read */
41ad427da   Alan Cox   digi_acceleport: ...
1324
  	ret = usb_submit_urb(urb, GFP_ATOMIC);
1f87158e4   Alan Stern   USB: remove refer...
1325
  	if (ret != 0 && ret != -EPERM) {
194343d93   Greg Kroah-Hartman   USB: remove use o...
1326
1327
1328
1329
  		dev_err(&port->dev,
  			"%s: failed resubmitting urb, ret=%d, port=%d
  ",
  			__func__, ret, priv->dp_port_num);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1330
1331
1332
  	}
  
  }
41ad427da   Alan Cox   digi_acceleport: ...
1333
1334
1335
1336
1337
1338
1339
1340
1341
  /*
   *  Digi Read INB Callback
   *
   *  Digi Read INB Callback handles reads on the in band ports, sending
   *  the data on to the tty subsystem.  When called we know port and
   *  port->private are not NULL and port->serial has been validated.
   *  It returns 0 if successful, 1 if successful but the port is
   *  throttled, and -1 if the sanity checks failed.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342

c6d61269f   Alan Cox   digi_acceleport: ...
1343
  static int digi_read_inb_callback(struct urb *urb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1344
  {
cdc977922   Ming Lei   USB: remove unnec...
1345
  	struct usb_serial_port *port = urb->context;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1346
  	struct digi_port *priv = usb_get_serial_port_data(port);
1b0aed2b1   Johan Hovold   USB: serial: digi...
1347
1348
1349
1350
1351
  	unsigned char *buf = urb->transfer_buffer;
  	int opcode;
  	int len;
  	int port_status;
  	unsigned char *data;
41ad427da   Alan Cox   digi_acceleport: ...
1352
  	int flag, throttled;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1353
1354
  
  	/* short/multiple packet check */
1b0aed2b1   Johan Hovold   USB: serial: digi...
1355
1356
1357
1358
1359
1360
1361
1362
  	if (urb->actual_length < 2) {
  		dev_warn(&port->dev, "short packet received
  ");
  		return -1;
  	}
  
  	opcode = buf[0];
  	len = buf[1];
c6d61269f   Alan Cox   digi_acceleport: ...
1363
  	if (urb->actual_length != len + 2) {
1b0aed2b1   Johan Hovold   USB: serial: digi...
1364
1365
1366
1367
1368
1369
1370
1371
1372
  		dev_err(&port->dev, "malformed packet received: port=%d, opcode=%d, len=%d, actual_length=%u
  ",
  			priv->dp_port_num, opcode, len, urb->actual_length);
  		return -1;
  	}
  
  	if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) {
  		dev_err(&port->dev, "malformed data packet received
  ");
c6d61269f   Alan Cox   digi_acceleport: ...
1373
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1374
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1375
  	spin_lock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1376
1377
1378
1379
  
  	/* check for throttle; if set, do not resubmit read urb */
  	/* indicate the read chain needs to be restarted on unthrottle */
  	throttled = priv->dp_throttled;
c6d61269f   Alan Cox   digi_acceleport: ...
1380
  	if (throttled)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1381
1382
1383
  		priv->dp_throttle_restart = 1;
  
  	/* receive data */
2e124b4a3   Jiri Slaby   TTY: switch tty_f...
1384
  	if (opcode == DIGI_CMD_RECEIVE_DATA) {
1b0aed2b1   Johan Hovold   USB: serial: digi...
1385
1386
  		port_status = buf[2];
  		data = &buf[3];
85d751071   Greg Kroah-Hartman   USB: serial: digi...
1387
  		/* get flag from port_status */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1388
1389
1390
  		flag = 0;
  
  		/* overrun is special, not associated with a char */
c6d61269f   Alan Cox   digi_acceleport: ...
1391
  		if (port_status & DIGI_OVERRUN_ERROR)
92a19f9ce   Jiri Slaby   TTY: switch tty_i...
1392
  			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1393
1394
1395
  
  		/* break takes precedence over parity, */
  		/* which takes precedence over framing errors */
c6d61269f   Alan Cox   digi_acceleport: ...
1396
  		if (port_status & DIGI_BREAK_ERROR)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397
  			flag = TTY_BREAK;
c6d61269f   Alan Cox   digi_acceleport: ...
1398
  		else if (port_status & DIGI_PARITY_ERROR)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
  			flag = TTY_PARITY;
c6d61269f   Alan Cox   digi_acceleport: ...
1400
  		else if (port_status & DIGI_FRAMING_ERROR)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
  			flag = TTY_FRAME;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402

85d751071   Greg Kroah-Hartman   USB: serial: digi...
1403
  		/* data length is len-1 (one byte of len is port_status) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
  		--len;
c6d61269f   Alan Cox   digi_acceleport: ...
1405
  		if (len > 0) {
2f6933571   Jiri Slaby   TTY: convert more...
1406
1407
  			tty_insert_flip_string_fixed_flag(&port->port, data,
  					flag, len);
2e124b4a3   Jiri Slaby   TTY: switch tty_f...
1408
  			tty_flip_buffer_push(&port->port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1411
  	spin_unlock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1412

c6d61269f   Alan Cox   digi_acceleport: ...
1413
  	if (opcode == DIGI_CMD_RECEIVE_DISABLE)
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1414
1415
  		dev_dbg(&port->dev, "%s: got RECEIVE_DISABLE
  ", __func__);
c6d61269f   Alan Cox   digi_acceleport: ...
1416
  	else if (opcode != DIGI_CMD_RECEIVE_DATA)
f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1417
1418
  		dev_dbg(&port->dev, "%s: unknown opcode: %d
  ", __func__, opcode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419

41ad427da   Alan Cox   digi_acceleport: ...
1420
  	return throttled ? 1 : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
1422
  
  }
41ad427da   Alan Cox   digi_acceleport: ...
1423
1424
1425
1426
1427
1428
1429
1430
  /*
   *  Digi Read OOB Callback
   *
   *  Digi Read OOB Callback handles reads on the out of band port.
   *  When called we know port and port->private are not NULL and
   *  the port->serial is valid.  It returns 0 if successful, and
   *  -1 if the sanity checks failed.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1431

c6d61269f   Alan Cox   digi_acceleport: ...
1432
  static int digi_read_oob_callback(struct urb *urb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
  {
cdc977922   Ming Lei   USB: remove unnec...
1434
  	struct usb_serial_port *port = urb->context;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
  	struct usb_serial *serial = port->serial;
4a90f09b2   Alan Cox   tty: usb-serial k...
1436
  	struct tty_struct *tty;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1437
  	struct digi_port *priv = usb_get_serial_port_data(port);
2d3808892   Johan Hovold   USB: serial: digi...
1438
  	unsigned char *buf = urb->transfer_buffer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1439
1440
  	int opcode, line, status, val;
  	int i;
41ad427da   Alan Cox   digi_acceleport: ...
1441
  	unsigned int rts;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1442

2d3808892   Johan Hovold   USB: serial: digi...
1443
1444
  	if (urb->actual_length < 4)
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1445
  	/* handle each oob command */
2f6821462   Johan Hovold   USB: serial: digi...
1446
  	for (i = 0; i < urb->actual_length - 3; i += 4) {
2d3808892   Johan Hovold   USB: serial: digi...
1447
1448
1449
1450
  		opcode = buf[i];
  		line = buf[i + 1];
  		status = buf[i + 2];
  		val = buf[i + 3];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1451

f9dfbebb8   Greg Kroah-Hartman   USB: serial: digi...
1452
1453
  		dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d
  ",
c6d61269f   Alan Cox   digi_acceleport: ...
1454
  			opcode, line, status, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1455

c6d61269f   Alan Cox   digi_acceleport: ...
1456
  		if (status != 0 || line >= serial->type->num_ports)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1457
1458
1459
  			continue;
  
  		port = serial->port[line];
41ad427da   Alan Cox   digi_acceleport: ...
1460
1461
  		priv = usb_get_serial_port_data(port);
  		if (priv == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1462
  			return -1;
4a90f09b2   Alan Cox   tty: usb-serial k...
1463
  		tty = tty_port_tty_get(&port->port);
4287341d4   Alan Cox   tty: Fix the digi...
1464

41ad427da   Alan Cox   digi_acceleport: ...
1465
  		rts = 0;
4287341d4   Alan Cox   tty: Fix the digi...
1466
  		if (tty)
9db276f8f   Peter Hurley   tty: Use termios ...
1467
  			rts = C_CRTSCTS(tty);
4287341d4   Alan Cox   tty: Fix the digi...
1468
  		if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
c6d61269f   Alan Cox   digi_acceleport: ...
1469
  			spin_lock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1470
  			/* convert from digi flags to termiox flags */
c6d61269f   Alan Cox   digi_acceleport: ...
1471
  			if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1472
1473
  				priv->dp_modem_signals |= TIOCM_CTS;
  				/* port must be open to use tty struct */
d95e3caea   Peter Hurley   usb: serial: Remo...
1474
  				if (rts)
6aad04f21   Jiri Slaby   TTY: add tty_port...
1475
  					tty_port_tty_wakeup(&port->port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1476
1477
1478
  			} else {
  				priv->dp_modem_signals &= ~TIOCM_CTS;
  				/* port must be open to use tty struct */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1479
  			}
c6d61269f   Alan Cox   digi_acceleport: ...
1480
  			if (val & DIGI_READ_INPUT_SIGNALS_DSR)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1481
1482
1483
  				priv->dp_modem_signals |= TIOCM_DSR;
  			else
  				priv->dp_modem_signals &= ~TIOCM_DSR;
c6d61269f   Alan Cox   digi_acceleport: ...
1484
  			if (val & DIGI_READ_INPUT_SIGNALS_RI)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1485
1486
1487
  				priv->dp_modem_signals |= TIOCM_RI;
  			else
  				priv->dp_modem_signals &= ~TIOCM_RI;
c6d61269f   Alan Cox   digi_acceleport: ...
1488
  			if (val & DIGI_READ_INPUT_SIGNALS_DCD)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1489
1490
1491
  				priv->dp_modem_signals |= TIOCM_CD;
  			else
  				priv->dp_modem_signals &= ~TIOCM_CD;
c6d61269f   Alan Cox   digi_acceleport: ...
1492
1493
1494
  			spin_unlock(&priv->dp_port_lock);
  		} else if (opcode == DIGI_CMD_TRANSMIT_IDLE) {
  			spin_lock(&priv->dp_port_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1495
  			priv->dp_transmit_idle = 1;
c6d61269f   Alan Cox   digi_acceleport: ...
1496
1497
1498
1499
  			wake_up_interruptible(&priv->dp_transmit_idle_wait);
  			spin_unlock(&priv->dp_port_lock);
  		} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
  			wake_up_interruptible(&priv->dp_flush_wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1500
  		}
4a90f09b2   Alan Cox   tty: usb-serial k...
1501
  		tty_kref_put(tty);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1502
  	}
c6d61269f   Alan Cox   digi_acceleport: ...
1503
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1504
1505
  
  }
68e241134   Greg Kroah-Hartman   USB: serial: rewo...
1506
  module_usb_serial_driver(serial_drivers, id_table_combined);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1507

c6d61269f   Alan Cox   digi_acceleport: ...
1508
1509
  MODULE_AUTHOR(DRIVER_AUTHOR);
  MODULE_DESCRIPTION(DRIVER_DESC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510
  MODULE_LICENSE("GPL");