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
		tty_insert_flip_string(tty, data, urb->actual_length);
L
Linus Torvalds 已提交
176 177
		tty_flip_buffer_push(tty);
	}
A
Alan Cox 已提交
178
	tty_kref_put(tty);
L
Linus Torvalds 已提交
179 180

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

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

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

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

A
Alan Cox 已提交
208 209 210 211 212 213 214 215 216 217 218 219
	/* --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 已提交
220
	if (result < 0)
A
Alan Cox 已提交
221 222
		dev_err(&port->dev,
			"Init of modem failed (error = %d)\n", result);
L
Linus Torvalds 已提交
223 224

	/* reset the bulk pipes */
A
Alan Cox 已提交
225 226 227 228
	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 已提交
229

A
Alan Cox 已提交
230 231
	/*--2: Start reading from the device */
	dbg("%s: setting up bulk read callback", __func__);
L
Linus Torvalds 已提交
232 233 234 235 236 237 238
	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 已提交
239 240
		dbg("%s - usb_submit_urb(read bulk) failed with status %d",
							__func__, result);
L
Linus Torvalds 已提交
241 242

	/*--3: Tell the modem to open the floodgates on the rx bulk channel */
A
Alan Cox 已提交
243
	dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__);
L
Linus Torvalds 已提交
244
	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
A
Alan Cox 已提交
245 246 247 248 249 250 251 252 253 254
			 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 已提交
255 256

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

L
Linus Torvalds 已提交
420 421 422 423 424 425 426 427 428
	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) {
429
		port->write_urb_busy = 0;
A
Alan Cox 已提交
430 431
		dbg("%s - usb_submit_urb(write bulk) failed with error = %d",
								__func__, ret);
L
Linus Torvalds 已提交
432 433 434
		return ret;
	}

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

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;
}

450
static struct usb_serial_driver ipw_device = {
451 452
	.driver = {
		.owner =	THIS_MODULE,
453
		.name =		"ipw",
454
	},
455
	.description =		"IPWireless converter",
456
	.usb_driver = 		&usb_ipw_driver,
L
Linus Torvalds 已提交
457 458 459 460
	.id_table =		usb_ipw_ids,
	.num_ports =		1,
	.open =			ipw_open,
	.close =		ipw_close,
461
	.dtr_rts =		ipw_dtr_rts,
L
Linus Torvalds 已提交
462 463 464 465 466 467 468 469 470
	.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,
};



471
static int __init usb_ipw_init(void)
L
Linus Torvalds 已提交
472 473 474 475 476 477 478 479 480 481 482
{
	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;
	}
483 484
	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
	       DRIVER_DESC "\n");
L
Linus Torvalds 已提交
485 486 487
	return 0;
}

488
static void __exit usb_ipw_exit(void)
L
Linus Torvalds 已提交
489 490 491 492 493 494 495 496 497
{
	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 已提交
498 499
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
L
Linus Torvalds 已提交
500 501 502 503
MODULE_LICENSE("GPL");

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