ipw.c 13.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * IPWireless 3G UMTS TDD Modem driver (USB connected)
 *
 *   Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
 *   Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
 *
 *   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.
 *
 * All information about the device was acquired using SnoopyPro
A
Alan Cox 已提交
13
 * on MSFT's O/S, and examing the MSFT drivers' debug output
L
Linus Torvalds 已提交
14 15 16 17 18 19
 * (insanely left _on_ in the enduser version)
 *
 * It was written out of frustration with the IPWireless USB modem
 * supplied by Axity3G/Sentech South Africa not supporting
 * Linux whatsoever.
 *
A
Alan Cox 已提交
20
 * Nobody provided any proprietary information that was not already
L
Linus Torvalds 已提交
21
 * available for this device.
A
Alan Cox 已提交
22 23 24
 *
 * The modem adheres to the "3GPP TS  27.007 AT command set for 3G
 * User Equipment (UE)" standard, available from
L
Linus Torvalds 已提交
25 26 27 28
 * http://www.3gpp.org/ftp/Specs/html-info/27007.htm
 *
 * The code was only tested the IPWireless handheld modem distributed
 * in South Africa by Sentech.
A
Alan Cox 已提交
29
 *
L
Linus Torvalds 已提交
30 31 32
 * It may work for Woosh Inc in .nz too, as it appears they use the
 * same kit.
 *
A
Alan Cox 已提交
33
 * There is still some work to be done in terms of handling
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
 * DCD, DTR, RTS, CTS which are currently faked.
 * It's good enough for PPP at this point. It's based off all kinds of
 * code found in usb/serial and usb/class
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
49
#include <linux/usb/serial.h>
A
Alan Cox 已提交
50
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

/*
 * Version Information
 */
#define DRIVER_VERSION	"v0.3"
#define DRIVER_AUTHOR	"Roelf Diedericks"
#define DRIVER_DESC	"IPWireless tty driver"

#define IPW_TTY_MAJOR	240	/* real device node major id, experimental range */
#define IPW_TTY_MINORS	256	/* we support 256 devices, dunno why, it'd be insane :) */

#define USB_IPW_MAGIC	0x6d02	/* magic number for ipw struct */


/* Message sizes */
#define EVENT_BUFFER_SIZE	0xFF
A
Alan Cox 已提交
67
#define CHAR2INT16(c1, c0)	(((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
L
Linus Torvalds 已提交
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
#define NUM_BULK_URBS		24
#define NUM_CONTROL_URBS	16

/* vendor/product pairs that are known work with this driver*/
#define IPW_VID		0x0bc3
#define IPW_PID		0x0001


/* Vendor commands: */

/* baud rates */
enum {
	ipw_sio_b256000 = 0x000e,
	ipw_sio_b128000 = 0x001d,
	ipw_sio_b115200 = 0x0020,
	ipw_sio_b57600  = 0x0040,
	ipw_sio_b56000  = 0x0042,
	ipw_sio_b38400  = 0x0060,
	ipw_sio_b19200  = 0x00c0,
	ipw_sio_b14400  = 0x0100,
	ipw_sio_b9600   = 0x0180,
	ipw_sio_b4800   = 0x0300,
	ipw_sio_b2400   = 0x0600,
	ipw_sio_b1200   = 0x0c00,
	ipw_sio_b600    = 0x1800
};

/* data bits */
#define ipw_dtb_7		0x700
A
Alan Cox 已提交
97 98
#define ipw_dtb_8		0x810	/* ok so the define is misleading, I know, but forces 8,n,1 */
					/* I mean, is there a point to any other setting these days? :) */
L
Linus Torvalds 已提交
99 100

/* usb control request types : */
A
Alan Cox 已提交
101 102 103 104 105 106 107 108 109 110
#define IPW_SIO_RXCTL		0x00	/* control bulk rx channel transmissions, value=1/0 (on/off) */
#define IPW_SIO_SET_BAUD	0x01	/* set baud, value=requested ipw_sio_bxxxx */
#define IPW_SIO_SET_LINE	0x03	/* set databits, parity. value=ipw_dtb_x */
#define IPW_SIO_SET_PIN		0x03	/* set/clear dtr/rts value=ipw_pin_xxx */
#define IPW_SIO_POLL		0x08	/* get serial port status byte, call with value=0 */
#define IPW_SIO_INIT		0x11	/* initializes ? value=0 (appears as first thing todo on open) */
#define IPW_SIO_PURGE		0x12	/* purge all transmissions?, call with value=numchar_to_purge */
#define IPW_SIO_HANDFLOW	0x13	/* set xon/xoff limits value=0, and a buffer of 0x10 bytes */
#define IPW_SIO_SETCHARS	0x13	/* set the flowcontrol special chars, value=0, buf=6 bytes, */
					/* last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13 */
L
Linus Torvalds 已提交
111 112 113 114 115

/* values used for request IPW_SIO_SET_PIN */
#define IPW_PIN_SETDTR		0x101
#define IPW_PIN_SETRTS		0x202
#define IPW_PIN_CLRDTR		0x100
A
Alan Cox 已提交
116
#define IPW_PIN_CLRRTS		0x200 /* unconfirmed */
L
Linus Torvalds 已提交
117 118 119 120 121 122

/* values used for request IPW_SIO_RXCTL */
#define IPW_RXBULK_ON		1
#define IPW_RXBULK_OFF		0

/* various 16 byte hardcoded transferbuffers used by flow control */
A
Alan Cox 已提交
123 124
#define IPW_BYTES_FLOWINIT	{ 0x01, 0, 0, 0, 0x40, 0, 0, 0, \
					0, 0, 0, 0, 0, 0, 0, 0 }
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133 134 135 136

/* Interpretation of modem status lines */
/* These need sorting out by individually connecting pins and checking
 * results. FIXME!
 * When data is being sent we see 0x30 in the lower byte; this must
 * contain DSR and CTS ...
 */
#define IPW_DSR			((1<<4) | (1<<5))
#define IPW_CTS			((1<<5) | (1<<4))

#define IPW_WANTS_TO_SEND	0x30

137
static const struct usb_device_id usb_ipw_ids[] = {
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147 148
	{ USB_DEVICE(IPW_VID, IPW_PID) },
	{ },
};

MODULE_DEVICE_TABLE(usb, usb_ipw_ids);

static struct usb_driver usb_ipw_driver = {
	.name =		"ipwtty",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	usb_ipw_ids,
149
	.no_dynamic_id = 	1,
L
Linus Torvalds 已提交
150 151 152 153
};

static int debug;

154
static void ipw_read_bulk_callback(struct urb *urb)
L
Linus Torvalds 已提交
155 156 157 158 159
{
	struct usb_serial_port *port = urb->context;
	unsigned char *data = urb->transfer_buffer;
	struct tty_struct *tty;
	int result;
160
	int status = urb->status;
L
Linus Torvalds 已提交
161

162
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
163

164 165
	if (status) {
		dbg("%s - nonzero read bulk status received: %d",
166
		    __func__, status);
L
Linus Torvalds 已提交
167 168 169
		return;
	}

A
Alan Cox 已提交
170 171
	usb_serial_debug_data(debug, &port->dev, __func__,
					urb->actual_length, data);
L
Linus Torvalds 已提交
172

A
Alan Cox 已提交
173
	tty = tty_port_tty_get(&port->port);
L
Linus Torvalds 已提交
174
	if (tty && urb->actual_length) {
A
Alan Cox 已提交
175 176
		tty_buffer_request_room(tty, urb->actual_length);
		tty_insert_flip_string(tty, data, urb->actual_length);
L
Linus Torvalds 已提交
177 178
		tty_flip_buffer_push(tty);
	}
A
Alan Cox 已提交
179
	tty_kref_put(tty);
L
Linus Torvalds 已提交
180 181

	/* Continue trying to always read  */
A
Alan Cox 已提交
182 183
	usb_fill_bulk_urb(port->read_urb, port->serial->dev,
			  usb_rcvbulkpipe(port->serial->dev,
L
Linus Torvalds 已提交
184
					   port->bulk_in_endpointAddress),
A
Alan Cox 已提交
185 186 187
			  port->read_urb->transfer_buffer,
			  port->read_urb->transfer_buffer_length,
			  ipw_read_bulk_callback, port);
L
Linus Torvalds 已提交
188 189
	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
	if (result)
A
Alan Cox 已提交
190 191 192
		dev_err(&port->dev,
			"%s - failed resubmitting read urb, error %d\n",
							__func__, result);
L
Linus Torvalds 已提交
193 194 195
	return;
}

196
static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
L
Linus Torvalds 已提交
197 198 199 200 201 202
{
	struct usb_device *dev = port->serial->dev;
	u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
	u8 *buf_flow_init;
	int result;

203
	dbg("%s", __func__);
L
Linus Torvalds 已提交
204

205
	buf_flow_init = kmemdup(buf_flow_static, 16, GFP_KERNEL);
L
Linus Torvalds 已提交
206 207 208
	if (!buf_flow_init)
		return -ENOMEM;

A
Alan Cox 已提交
209 210 211 212 213 214 215 216 217 218 219 220
	/* --1: Tell the modem to initialize (we think) From sniffs this is
	 *	always the first thing that gets sent to the modem during
	 *	opening of the device */
	dbg("%s: Sending SIO_INIT (we guess)", __func__);
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			 IPW_SIO_INIT,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 0,
			 0, /* index */
			 NULL,
			 0,
			 100000);
L
Linus Torvalds 已提交
221
	if (result < 0)
A
Alan Cox 已提交
222 223
		dev_err(&port->dev,
			"Init of modem failed (error = %d)\n", result);
L
Linus Torvalds 已提交
224 225

	/* reset the bulk pipes */
A
Alan Cox 已提交
226 227 228 229
	usb_clear_halt(dev,
			usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
	usb_clear_halt(dev,
			usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
L
Linus Torvalds 已提交
230

A
Alan Cox 已提交
231 232
	/*--2: Start reading from the device */
	dbg("%s: setting up bulk read callback", __func__);
L
Linus Torvalds 已提交
233 234 235 236 237 238 239
	usb_fill_bulk_urb(port->read_urb, dev,
			  usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
			  port->bulk_in_buffer,
			  port->bulk_in_size,
			  ipw_read_bulk_callback, port);
	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
	if (result < 0)
A
Alan Cox 已提交
240 241
		dbg("%s - usb_submit_urb(read bulk) failed with status %d",
							__func__, result);
L
Linus Torvalds 已提交
242 243

	/*--3: Tell the modem to open the floodgates on the rx bulk channel */
A
Alan Cox 已提交
244
	dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__);
L
Linus Torvalds 已提交
245
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
246 247 248 249 250 251 252 253 254 255
			 IPW_SIO_RXCTL,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 IPW_RXBULK_ON,
			 0, /* index */
			 NULL,
			 0,
			 100000);
	if (result < 0)
		dev_err(&port->dev,
			"Enabling bulk RxRead failed (error = %d)\n", result);
L
Linus Torvalds 已提交
256 257

	/*--4: setup the initial flowcontrol */
A
Alan Cox 已提交
258
	dbg("%s:setting init flowcontrol (%s)", __func__, buf_flow_init);
L
Linus Torvalds 已提交
259
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
260 261 262 263 264 265 266
			 IPW_SIO_HANDFLOW,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 0,
			 0,
			 buf_flow_init,
			 0x10,
			 200000);
L
Linus Torvalds 已提交
267
	if (result < 0)
A
Alan Cox 已提交
268 269
		dev_err(&port->dev,
			"initial flowcontrol failed (error = %d)\n", result);
L
Linus Torvalds 已提交
270 271 272


	/*--5: raise the dtr */
A
Alan Cox 已提交
273
	dbg("%s:raising dtr", __func__);
L
Linus Torvalds 已提交
274
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
275 276 277 278 279 280 281
			 IPW_SIO_SET_PIN,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 IPW_PIN_SETDTR,
			 0,
			 NULL,
			 0,
			 200000);
L
Linus Torvalds 已提交
282
	if (result < 0)
A
Alan Cox 已提交
283 284
		dev_err(&port->dev,
				"setting dtr failed (error = %d)\n", result);
L
Linus Torvalds 已提交
285 286

	/*--6: raise the rts */
A
Alan Cox 已提交
287
	dbg("%s:raising rts", __func__);
L
Linus Torvalds 已提交
288
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
289 290 291 292 293 294 295
			 IPW_SIO_SET_PIN,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 IPW_PIN_SETRTS,
			 0,
			 NULL,
			 0,
			 200000);
L
Linus Torvalds 已提交
296
	if (result < 0)
A
Alan Cox 已提交
297 298 299
		dev_err(&port->dev,
				"setting dtr failed (error = %d)\n", result);

L
Linus Torvalds 已提交
300 301 302 303
	kfree(buf_flow_init);
	return 0;
}

304
static void ipw_dtr_rts(struct usb_serial_port *port, int on)
L
Linus Torvalds 已提交
305 306 307 308 309
{
	struct usb_device *dev = port->serial->dev;
	int result;

	/*--1: drop the dtr */
A
Alan Cox 已提交
310
	dbg("%s:dropping dtr", __func__);
L
Linus Torvalds 已提交
311
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
312 313
			 IPW_SIO_SET_PIN,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
314
			 on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
A
Alan Cox 已提交
315 316 317 318
			 0,
			 NULL,
			 0,
			 200000);
L
Linus Torvalds 已提交
319
	if (result < 0)
A
Alan Cox 已提交
320 321
		dev_err(&port->dev, "dropping dtr failed (error = %d)\n",
								result);
L
Linus Torvalds 已提交
322 323

	/*--2: drop the rts */
A
Alan Cox 已提交
324
	dbg("%s:dropping rts", __func__);
L
Linus Torvalds 已提交
325
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
326 327
			 IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
			 		USB_RECIP_INTERFACE | USB_DIR_OUT,
328
			 on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
A
Alan Cox 已提交
329 330 331 332
			 0,
			 NULL,
			 0,
			 200000);
L
Linus Torvalds 已提交
333
	if (result < 0)
A
Alan Cox 已提交
334 335
		dev_err(&port->dev,
				"dropping rts failed (error = %d)\n", result);
336
}
L
Linus Torvalds 已提交
337

338 339 340 341
static void ipw_close(struct usb_serial_port *port)
{
	struct usb_device *dev = port->serial->dev;
	int result;
L
Linus Torvalds 已提交
342 343

	/*--3: purge */
A
Alan Cox 已提交
344
	dbg("%s:sending purge", __func__);
L
Linus Torvalds 已提交
345
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
346 347 348 349 350 351 352
			 IPW_SIO_PURGE, USB_TYPE_VENDOR |
			 		USB_RECIP_INTERFACE | USB_DIR_OUT,
			 0x03,
			 0,
			 NULL,
			 0,
			 200000);
L
Linus Torvalds 已提交
353
	if (result < 0)
354
		dev_err(&port->dev, "purge failed (error = %d)\n", result);
L
Linus Torvalds 已提交
355 356


A
Alan Cox 已提交
357 358
	/* send RXBULK_off (tell modem to stop transmitting bulk data on
	   rx chan) */
L
Linus Torvalds 已提交
359
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
360 361 362 363 364 365 366
			 IPW_SIO_RXCTL,
			 USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
			 IPW_RXBULK_OFF,
			 0, /* index */
			 NULL,
			 0,
			 100000);
L
Linus Torvalds 已提交
367 368

	if (result < 0)
A
Alan Cox 已提交
369 370
		dev_err(&port->dev,
			"Disabling bulk RxRead failed (error = %d)\n", result);
L
Linus Torvalds 已提交
371 372 373 374 375 376

	/* shutdown any in-flight urbs that we know about */
	usb_kill_urb(port->read_urb);
	usb_kill_urb(port->write_urb);
}

377
static void ipw_write_bulk_callback(struct urb *urb)
L
Linus Torvalds 已提交
378 379
{
	struct usb_serial_port *port = urb->context;
380
	int status = urb->status;
L
Linus Torvalds 已提交
381

382
	dbg("%s", __func__);
L
Linus Torvalds 已提交
383

B
Bart Oldeman 已提交
384 385
	port->write_urb_busy = 0;

386 387
	if (status)
		dbg("%s - nonzero write bulk status received: %d",
388
		    __func__, status);
L
Linus Torvalds 已提交
389

390
	usb_serial_port_softint(port);
L
Linus Torvalds 已提交
391 392
}

A
Alan Cox 已提交
393 394
static int ipw_write(struct tty_struct *tty, struct usb_serial_port *port,
					const unsigned char *buf, int count)
L
Linus Torvalds 已提交
395 396 397 398
{
	struct usb_device *dev = port->serial->dev;
	int ret;

399
	dbg("%s: TOP: count=%d, in_interrupt=%ld", __func__,
A
Alan Cox 已提交
400
		count, in_interrupt());
L
Linus Torvalds 已提交
401 402

	if (count == 0) {
403
		dbg("%s - write request of 0 bytes", __func__);
L
Linus Torvalds 已提交
404 405
		return 0;
	}
406

407
	spin_lock_bh(&port->lock);
408
	if (port->write_urb_busy) {
409
		spin_unlock_bh(&port->lock);
410
		dbg("%s - already writing", __func__);
L
Linus Torvalds 已提交
411
		return 0;
412 413
	}
	port->write_urb_busy = 1;
414
	spin_unlock_bh(&port->lock);
L
Linus Torvalds 已提交
415 416 417 418

	count = min(count, port->bulk_out_size);
	memcpy(port->bulk_out_buffer, buf, count);

419
	dbg("%s count now:%d", __func__, count);
420

L
Linus Torvalds 已提交
421 422 423 424 425 426 427 428 429
	usb_fill_bulk_urb(port->write_urb, dev,
			  usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
			  port->write_urb->transfer_buffer,
			  count,
			  ipw_write_bulk_callback,
			  port);

	ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
	if (ret != 0) {
430
		port->write_urb_busy = 0;
A
Alan Cox 已提交
431 432
		dbg("%s - usb_submit_urb(write bulk) failed with error = %d",
								__func__, ret);
L
Linus Torvalds 已提交
433 434 435
		return ret;
	}

436
	dbg("%s returning %d", __func__, count);
L
Linus Torvalds 已提交
437
	return count;
A
Alan Cox 已提交
438
}
L
Linus Torvalds 已提交
439 440 441 442 443 444 445 446 447 448 449 450

static int ipw_probe(struct usb_serial_port *port)
{
	return 0;
}

static int ipw_disconnect(struct usb_serial_port *port)
{
	usb_set_serial_port_data(port, NULL);
	return 0;
}

451
static struct usb_serial_driver ipw_device = {
452 453
	.driver = {
		.owner =	THIS_MODULE,
454
		.name =		"ipw",
455
	},
456
	.description =		"IPWireless converter",
457
	.usb_driver = 		&usb_ipw_driver,
L
Linus Torvalds 已提交
458 459 460 461
	.id_table =		usb_ipw_ids,
	.num_ports =		1,
	.open =			ipw_open,
	.close =		ipw_close,
462
	.dtr_rts =		ipw_dtr_rts,
L
Linus Torvalds 已提交
463 464 465 466 467 468 469 470 471
	.port_probe = 		ipw_probe,
	.port_remove =		ipw_disconnect,
	.write =		ipw_write,
	.write_bulk_callback =	ipw_write_bulk_callback,
	.read_bulk_callback =	ipw_read_bulk_callback,
};



472
static int __init usb_ipw_init(void)
L
Linus Torvalds 已提交
473 474 475 476 477 478 479 480 481 482 483
{
	int retval;

	retval = usb_serial_register(&ipw_device);
	if (retval)
		return retval;
	retval = usb_register(&usb_ipw_driver);
	if (retval) {
		usb_serial_deregister(&ipw_device);
		return retval;
	}
484 485
	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
	       DRIVER_DESC "\n");
L
Linus Torvalds 已提交
486 487 488
	return 0;
}

489
static void __exit usb_ipw_exit(void)
L
Linus Torvalds 已提交
490 491 492 493 494 495 496 497 498
{
	usb_deregister(&usb_ipw_driver);
	usb_serial_deregister(&ipw_device);
}

module_init(usb_ipw_init);
module_exit(usb_ipw_exit);

/* Module information */
A
Alan Cox 已提交
499 500
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
L
Linus Torvalds 已提交
501 502 503 504
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");