summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/net/irda/mcp2120-sir.c
blob: 2e33f91bfe8f60dc261698028b0e85122d167849 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*********************************************************************
 *            
 *    
 * Filename:      mcp2120.c
 * Version:       1.0
 * Description:   Implementation for the MCP2120 (Microchip)
 * Status:        Experimental.
 * Author:        Felix Tang (tangf@eyetap.org)
 * Created at:    Sun Mar 31 19:32:12 EST 2002
 * Based on code by:   Dag Brattli <dagb@cs.uit.no>
 * 
 *     Copyright (c) 2002 Felix Tang, All Rights Reserved.
 *      
 *     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.
 *  
 ********************************************************************/

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>

#include <net/irda/irda.h>

#include "sir-dev.h"

static int mcp2120_reset(struct sir_dev *dev);
static int mcp2120_open(struct sir_dev *dev);
static int mcp2120_close(struct sir_dev *dev);
static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed);

#define MCP2120_9600    0x87
#define MCP2120_19200   0x8B
#define MCP2120_38400   0x85
#define MCP2120_57600   0x83
#define MCP2120_115200  0x81

#define MCP2120_COMMIT  0x11

static struct dongle_driver mcp2120 = {
	.owner		= THIS_MODULE,
	.driver_name	= "Microchip MCP2120",
	.type		= IRDA_MCP2120_DONGLE,
	.open		= mcp2120_open,
	.close		= mcp2120_close,
	.reset		= mcp2120_reset,
	.set_speed	= mcp2120_change_speed,
};

static int __init mcp2120_sir_init(void)
{
	return irda_register_dongle(&mcp2120);
}

static void __exit mcp2120_sir_cleanup(void)
{
	irda_unregister_dongle(&mcp2120);
}

static int mcp2120_open(struct sir_dev *dev)
{
	struct qos_info *qos = &dev->qos;

	/* seems no explicit power-on required here and reset switching it on anyway */

	qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
	qos->min_turn_time.bits = 0x01;
	irda_qos_bits_to_value(qos);

	return 0;
}

static int mcp2120_close(struct sir_dev *dev)
{
	/* Power off dongle */
        /* reset and inhibit mcp2120 */
	sirdev_set_dtr_rts(dev, TRUE, TRUE);
	// sirdev_set_dtr_rts(dev, FALSE, FALSE);

	return 0;
}

/*
 * Function mcp2120_change_speed (dev, speed)
 *
 *    Set the speed for the MCP2120.
 *
 */

#define MCP2120_STATE_WAIT_SPEED	(SIRDEV_STATE_DONGLE_SPEED+1)

static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed)
{
	unsigned state = dev->fsm.substate;
	unsigned delay = 0;
	u8 control[2];
	static int ret = 0;

	switch (state) {
	case SIRDEV_STATE_DONGLE_SPEED:
		/* Set DTR to enter command mode */
		sirdev_set_dtr_rts(dev, TRUE, FALSE);
                udelay(500);

		ret = 0;
		switch (speed) {
		default:
			speed = 9600;
			ret = -EINVAL;
			/* fall through */
		case 9600:
			control[0] = MCP2120_9600;
                        //printk("mcp2120 9600\n");
			break;
		case 19200:
			control[0] = MCP2120_19200;
                        //printk("mcp2120 19200\n");
			break;
		case 34800:
			control[0] = MCP2120_38400;
                        //printk("mcp2120 38400\n");
			break;
		case 57600:
			control[0] = MCP2120_57600;
                        //printk("mcp2120 57600\n");
			break;
		case 115200:
                        control[0] = MCP2120_115200;
                        //printk("mcp2120 115200\n");
			break;
		}
		control[1] = MCP2120_COMMIT;
	
		/* Write control bytes */
		sirdev_raw_write(dev, control, 2);
		dev->speed = speed;

		state = MCP2120_STATE_WAIT_SPEED;
		delay = 100;
                //printk("mcp2120_change_speed: dongle_speed\n");
		break;

	case MCP2120_STATE_WAIT_SPEED:
		/* Go back to normal mode */
		sirdev_set_dtr_rts(dev, FALSE, FALSE);
                //printk("mcp2120_change_speed: mcp_wait\n");
		break;

	default:
		net_err_ratelimited("%s(), undefine state %d\n",
				    __func__, state);
		ret = -EINVAL;
		break;
	}
	dev->fsm.substate = state;
	return (delay > 0) ? delay : ret;
}

/*
 * Function mcp2120_reset (driver)
 *
 *      This function resets the mcp2120 dongle.
 *      
 *      Info: -set RTS to reset mcp2120
 *            -set DTR to set mcp2120 software command mode
 *            -mcp2120 defaults to 9600 baud after reset
 *
 *      Algorithm:
 *      0. Set RTS to reset mcp2120.
 *      1. Clear RTS and wait for device reset timer of 30 ms (max).
 *      
 */

#define MCP2120_STATE_WAIT1_RESET	(SIRDEV_STATE_DONGLE_RESET+1)
#define MCP2120_STATE_WAIT2_RESET	(SIRDEV_STATE_DONGLE_RESET+2)

static int mcp2120_reset(struct sir_dev *dev)
{
	unsigned state = dev->fsm.substate;
	unsigned delay = 0;
	int ret = 0;

	switch (state) {
	case SIRDEV_STATE_DONGLE_RESET:
                //printk("mcp2120_reset: dongle_reset\n");
		/* Reset dongle by setting RTS*/
		sirdev_set_dtr_rts(dev, TRUE, TRUE);
		state = MCP2120_STATE_WAIT1_RESET;
		delay = 50;
		break;

	case MCP2120_STATE_WAIT1_RESET:
                //printk("mcp2120_reset: mcp2120_wait1\n");
                /* clear RTS and wait for at least 30 ms. */
		sirdev_set_dtr_rts(dev, FALSE, FALSE);
		state = MCP2120_STATE_WAIT2_RESET;
		delay = 50;
		break;

	case MCP2120_STATE_WAIT2_RESET:
                //printk("mcp2120_reset mcp2120_wait2\n");
		/* Go back to normal mode */
		sirdev_set_dtr_rts(dev, FALSE, FALSE);
		break;

	default:
		net_err_ratelimited("%s(), undefined state %d\n",
				    __func__, state);
		ret = -EINVAL;
		break;
	}
	dev->fsm.substate = state;
	return (delay > 0) ? delay : ret;
}

MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>");
MODULE_DESCRIPTION("Microchip MCP2120");
MODULE_LICENSE("GPL");
MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */

module_init(mcp2120_sir_init);
module_exit(mcp2120_sir_cleanup);