Subversion Repositories Code-Repo

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
260 Kevin 1
#include "defines.h"
2
#include "I2C1.h"
3
 
4
static I2C1_DATA *i2c_data_p;
273 Kevin 5
extern BTN_STATUS btns;
260 Kevin 6
 
7
// Set up the data structures for the base_I2C.code
8
// Should be called once before any i2c routines are called
9
void I2C1_Init(I2C1_DATA *data) {
10
    i2c_data_p = data;
11
 
12
    i2c_data_p->buffer_in_len = 0;
13
    i2c_data_p->buffer_in_len_tmp = 0;
14
    i2c_data_p->buffer_in_read_ind = 0;
15
    i2c_data_p->buffer_in_write_ind = 0;
16
 
17
    i2c_data_p->buffer_out_ind = 0;
18
    i2c_data_p->buffer_out_len = 0;
19
 
20
    i2c_data_p->operating_mode = 0;
21
    i2c_data_p->operating_state = I2C_IDLE;
22
    i2c_data_p->return_status = 0;
23
 
24
    i2c_data_p->slave_in_last_byte = 0;
25
    i2c_data_p->slave_sending_data = 0;
26
 
27
    i2c_data_p->master_dest_addr = 0;
28
    i2c_data_p->master_status = I2C_MASTER_IDLE;
29
 
30
    // Enable I2C interrupt
31
    PIE1bits.SSP1IE = 1;
32
}
33
 
34
// Setup the PIC to operate as a master.
35
void I2C1_Configure_Master(uint8_t speed) {
36
    i2c_data_p->operating_mode = I2C_MODE_MASTER;
37
 
38
    I2C_1_CLK_TRIS = 1;
39
    I2C_1_DAT_TRIS = 1;
40
 
41
    SSP1STAT = 0x0;
42
    SSP1CON1 = 0x0;
43
    SSP1CON2 = 0x0;
277 Kevin 44
    SSP1CON3 = 0x0;
260 Kevin 45
    SSP1CON1bits.SSPM = 0x8; // I2C Master Mode
275 Kevin 46
    if (speed == 0x01) {
47
        SSP1ADD = 0x13;         // Operate at 400KHz (32MHz)
48
        SSP1STATbits.SMP = 1;    // Disable Slew Rate Control
49
    } else if (speed == 0x02) {
50
        SSP1ADD = 0x07;         // Operate at 1Mhz (32Mhz)
51
        SSP1STATbits.SMP = 1;    // Disable Slew Rate Control
260 Kevin 52
    } else {
275 Kevin 53
        SSP1ADD = 0x4F;         // Operate at 100KHz (32MHz)
54
        SSP1STATbits.SMP = 0;    // Enable Slew Rate Control
260 Kevin 55
    }
56
    SSP1CON1bits.SSPEN = 1;  // Enable MSSP1 Module
57
}
58
 
59
// Sends length number of bytes in msg to specified address (no R/W bit)
60
void I2C1_Master_Send(uint8_t address, uint8_t length, uint8_t *msg) {
61
    uint8_t i;
62
    if (length == 0)
63
        return;
64
 
65
    // Copy message to send into buffer and save length/address
66
    for (i = 0; i < length; i++) {
67
        i2c_data_p->buffer_in[i] = msg[i];
68
    }
69
    i2c_data_p->buffer_in_len = length;
70
    i2c_data_p->master_dest_addr = address;
71
    i2c_data_p->buffer_in_read_ind = 0;
72
    i2c_data_p->buffer_in_write_ind = 0;
73
 
74
    // Change status to 'next' operation
75
    i2c_data_p->operating_state = I2C_SEND_ADDR;
76
    i2c_data_p->master_status = I2C_MASTER_SEND;
77
 
78
    // Generate start condition
79
    SSP1CON2bits.SEN = 1;
80
}
81
 
82
// Reads length number of bytes from address (no R/W bit)
83
void I2C1_Master_Recv(uint8_t address, uint8_t length) {
84
    if (length == 0)
85
        return;
86
 
87
    // Save length and address to get data from
88
    i2c_data_p->buffer_in_len = length;
89
    i2c_data_p->master_dest_addr = address;
90
    i2c_data_p->buffer_in_read_ind = 0;
91
    i2c_data_p->buffer_in_write_ind = 0;
92
 
93
    // Change status to 'next' operation
94
    i2c_data_p->operating_state = I2C_SEND_ADDR;
95
    i2c_data_p->master_status = I2C_MASTER_RECV;
96
 
97
    // Generate start condition
98
    SSP1CON2bits.SEN = 1;
99
}
100
 
101
// Writes msg to address then reads length number of bytes from address
102
void I2C1_Master_Restart(uint8_t address, uint8_t msg, uint8_t length) {
103
    uint8_t c;
104
    if (length == 0) {
105
        c = msg;
106
        I2C1_Master_Send(address, 1, &c);
107
        return;
108
    }
109
 
110
    // Save length and address to get data from
111
    i2c_data_p->buffer_in[0] = msg;
112
    i2c_data_p->buffer_in_len = length;
113
    i2c_data_p->master_dest_addr = address;
114
    i2c_data_p->buffer_in_read_ind = 0;
115
    i2c_data_p->buffer_in_write_ind = 0;
116
 
117
    // Change status to 'next' operation
118
    i2c_data_p->operating_state = I2C_SEND_ADDR;
119
    i2c_data_p->master_status = I2C_MASTER_RESTART;
120
 
121
    // Generate start condition
122
    SSP1CON2bits.SEN = 1;
123
}
124
 
125
// Setup the PIC to operate as a slave. The address must not include the R/W bit
126
void I2C1_Configure_Slave(uint8_t addr) {
127
    i2c_data_p->operating_mode = I2C_MODE_SLAVE;
128
 
129
    // Ensure the two lines are set for input (we are a slave)
130
    I2C_1_CLK_TRIS = 1;
131
    I2C_1_DAT_TRIS = 1;
132
 
133
    SSP1ADD = addr << 1;     // Set the slave address
134
 
135
    SSP1STAT = 0x0;
136
    SSP1CON1 = 0x0;
137
    SSP1CON2 = 0x0;
277 Kevin 138
    SSP1CON3 = 0x0;
139
    SSP1CON1bits.SSPM = 0x6; // Enable Slave 7-bit address
260 Kevin 140
    SSP1STATbits.SMP = 1;    // Slew Off
141
    SSP1CON2bits.SEN = 1;    // Enable clock-stretching
277 Kevin 142
    SSP1CON3bits.PCIE = 1;   // Interrupt on stop condition
143
    SSP1CON3bits.SCIE = 0;   // Disable interrupt on start condition
260 Kevin 144
    SSP1CON1bits.SSPEN = 1;  // Enable MSSP1 Module
145
}
146
 
147
void I2C1_Interrupt_Handler() {
148
    // Call interrupt depending on which mode we are operating in
149
    if (i2c_data_p->operating_mode == I2C_MODE_MASTER) {
150
        I2C1_Interrupt_Master();
151
    } else if (i2c_data_p->operating_mode == I2C_MODE_SLAVE) {
152
        I2C1_Interrupt_Slave();
153
    }
154
}
155
 
156
// An internal subroutine used in the master version of the i2c_interrupt_handler
157
void I2C1_Interrupt_Master() {
158
    // If we are in the middle of sending data
159
    if (i2c_data_p->master_status == I2C_MASTER_SEND) {
160
        switch (i2c_data_p->operating_state) {
161
            case I2C_IDLE:
162
                break;
163
            case I2C_SEND_ADDR:
164
                // Send the address with read bit set
165
                i2c_data_p->operating_state = I2C_CHECK_ACK_SEND;
166
                SSP1BUF = (i2c_data_p->master_dest_addr << 1) | 0x0;
167
                break;
168
            case I2C_CHECK_ACK_SEND:
169
                // Check if ACK is received or not
170
                if (!SSP1CON2bits.ACKSTAT) {
171
                    // If an ACK is received, send next byte of data
172
                    if (i2c_data_p->buffer_in_read_ind < i2c_data_p->buffer_in_len) {
173
                        SSP1BUF = i2c_data_p->buffer_in[i2c_data_p->buffer_in_read_ind];
174
                        i2c_data_p->buffer_in_read_ind++;
175
                    } else {
176
                        // If no more data is to be sent, send stop bit
177
                        i2c_data_p->operating_state = I2C_IDLE;
178
                        SSP1CON2bits.PEN = 1;
179
                        i2c_data_p->master_status = I2C_MASTER_IDLE;
180
                        i2c_data_p->return_status = I2C_SEND_OK;
181
                    }
182
                } else {
183
                    // If a NACK is received, stop transmission and send error
184
                    i2c_data_p->operating_state = I2C_IDLE;
185
                    SSP1CON2bits.PEN = 1;
186
                    i2c_data_p->master_status = I2C_MASTER_IDLE;
187
                    i2c_data_p->return_status = I2C_SEND_FAIL;
188
                }
189
                break;
190
        }
191
    // If we are in the middle of receiving data
192
    } else if (i2c_data_p->master_status == I2C_MASTER_RECV) {
193
        switch (i2c_data_p->operating_state) {
194
            case I2C_IDLE:
195
                break;
196
            case I2C_SEND_ADDR:
197
                // Send address with write bit set
198
                i2c_data_p->operating_state = I2C_CHECK_ACK_RECV;
199
                uint8_t tmp = (i2c_data_p->master_dest_addr << 1);
200
                tmp |= 0x01;
201
                SSP1BUF = tmp;
202
                break;
203
            case I2C_CHECK_ACK_RECV:
204
                // Check if ACK is received
205
                if (!SSP1CON2bits.ACKSTAT) {
206
                    // If an ACK is received, set module to receive 1 byte of data
207
                    i2c_data_p->operating_state = I2C_RCV_DATA;
208
                    SSP1CON2bits.RCEN = 1;
209
                } else {
210
                    // If a NACK is received, stop transmission and send error
211
                    i2c_data_p->operating_state = I2C_IDLE;
212
                    SSP1CON2bits.PEN = 1;
213
                    i2c_data_p->master_status = I2C_MASTER_IDLE;
214
                    i2c_data_p->return_status = I2C_RECV_FAIL;
215
                }
216
                break;
217
            case I2C_RCV_DATA:
218
                // On receive, save byte into buffer
219
                // TODO: Handle I2C buffer overflow
220
                i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = SSP1BUF;
221
                i2c_data_p->buffer_in_write_ind++;
222
                if (i2c_data_p->buffer_in_write_ind < i2c_data_p->buffer_in_len) {
223
                    // If we still need to read, send an ACK to the slave
224
                    i2c_data_p->operating_state = I2C_REQ_DATA;
225
                    SSP1CON2bits.ACKDT = 0;  // ACK
226
                    SSP1CON2bits.ACKEN = 1;
227
                } else {
228
                    // If we are done reading, send an NACK to the slave
229
                    i2c_data_p->operating_state = I2C_SEND_STOP;
230
                    SSP1CON2bits.ACKDT = 1;  // NACK
231
                    SSP1CON2bits.ACKEN = 1;
232
                }
233
                break;
234
            case I2C_REQ_DATA:
235
                // Set module to receive one byte of data
236
                i2c_data_p->operating_state = I2C_RCV_DATA;
237
                SSP1CON2bits.RCEN = 1;
238
                break;
239
            case I2C_SEND_STOP:
240
                // Send the stop bit and copy message to send to Main()
241
                i2c_data_p->operating_state = I2C_IDLE;
242
                SSP1CON2bits.PEN = 1;
243
                i2c_data_p->master_status = I2C_MASTER_IDLE;
244
                i2c_data_p->return_status = I2C_RECV_OK;
245
                break;
246
        }
247
    } else if (i2c_data_p->master_status == I2C_MASTER_RESTART) {
248
        switch (i2c_data_p->operating_state) {
249
            case I2C_IDLE:
250
                break;
251
            case I2C_SEND_ADDR:
252
                // Send the address with read bit set
253
                i2c_data_p->operating_state = I2C_CHECK_ACK_SEND;
254
                SSP1BUF = (i2c_data_p->master_dest_addr << 1) | 0x0;
255
                break;
256
            case I2C_CHECK_ACK_SEND:
257
                // Check if ACK is received or not
258
                if (!SSP1CON2bits.ACKSTAT) {
259
                    // If an ACK is received, send first byte of data
260
                    SSP1BUF = i2c_data_p->buffer_in[0];
261
                    i2c_data_p->operating_state = I2C_CHECK_ACK_RESTART;
262
                } else {
263
                    // If a NACK is received, stop transmission and send error
264
                    i2c_data_p->operating_state = I2C_IDLE;
265
                    SSP1CON2bits.PEN = 1;
266
                    i2c_data_p->master_status = I2C_MASTER_IDLE;
267
                    i2c_data_p->return_status = I2C_SEND_FAIL;
268
                }
269
                break;
270
            case I2C_CHECK_ACK_RESTART:
271
                if (!SSP1CON2bits.ACKSTAT) {
272
                    SSP1CON2bits.RSEN = 1;
273
                    i2c_data_p->operating_state = I2C_SEND_ADDR_2;
274
                } else {
275
                    // If a NACK is received, stop transmission and send error
276
                    i2c_data_p->operating_state = I2C_IDLE;
277
                    SSP1CON2bits.PEN = 1;
278
                    i2c_data_p->master_status = I2C_MASTER_IDLE;
279
                    i2c_data_p->return_status = I2C_SEND_FAIL;
280
                }
281
                break;
282
            case I2C_SEND_ADDR_2:
283
                // Send the address with read bit set
284
                i2c_data_p->operating_state = I2C_CHECK_ACK_RECV;
285
                uint8_t tmp = (i2c_data_p->master_dest_addr << 1);
286
                tmp |= 0x01;
287
                SSP1BUF = tmp;
288
                break;
289
            case I2C_CHECK_ACK_RECV:
290
                // Check if ACK is received
291
                if (!SSP1CON2bits.ACKSTAT) {
292
                    // If an ACK is received, set module to receive 1 byte of data
293
                    i2c_data_p->operating_state = I2C_RCV_DATA;
294
                    SSP1CON2bits.RCEN = 1;
295
                } else {
296
                    // If a NACK is received, stop transmission and send error
297
                    i2c_data_p->operating_state = I2C_IDLE;
298
                    SSP1CON2bits.PEN = 1;
299
                    i2c_data_p->master_status = I2C_MASTER_IDLE;
300
                    i2c_data_p->return_status = I2C_RECV_FAIL;
301
                }
302
                break;
303
            case I2C_RCV_DATA:
304
                // On receive, save byte into buffer
305
                // TODO: Handle I2C buffer overflow
306
                i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = SSP1BUF;
307
                i2c_data_p->buffer_in_write_ind++;
308
                if (i2c_data_p->buffer_in_write_ind < i2c_data_p->buffer_in_len) {
309
                    // If we still need to read, send an ACK to the slave
310
                    i2c_data_p->operating_state = I2C_REQ_DATA;
311
                    SSP1CON2bits.ACKDT = 0;  // ACK
312
                    SSP1CON2bits.ACKEN = 1;
313
                } else {
314
                    // If we are done reading, send an NACK to the slave
315
                    i2c_data_p->operating_state = I2C_SEND_STOP;
316
                    SSP1CON2bits.ACKDT = 1;  // NACK
317
                    SSP1CON2bits.ACKEN = 1;
318
                }
319
                break;
320
            case I2C_REQ_DATA:
321
                // Set module to receive one byte of data
322
                i2c_data_p->operating_state = I2C_RCV_DATA;
323
                SSP1CON2bits.RCEN = 1;
324
                break;
325
            case I2C_SEND_STOP:
326
                // Send the stop bit
327
                i2c_data_p->operating_state = I2C_IDLE;
328
                SSP1CON2bits.PEN = 1;
329
                i2c_data_p->master_status = I2C_MASTER_IDLE;
330
                i2c_data_p->return_status = I2C_RECV_OK;
331
                break;
332
        }
333
    }
334
}
335
 
336
void I2C1_Interrupt_Slave() {
337
    uint8_t received_data;
338
    uint8_t data_read_from_buffer = 0;
339
    uint8_t data_written_to_buffer = 0;
340
    uint8_t overrun_error = 0;
341
 
342
    // Clear SSPOV (overflow bit)
343
    if (SSP1CON1bits.SSPOV == 1) {
344
        SSP1CON1bits.SSPOV = 0;
345
        // We failed to read the buffer in time, so we know we
346
        //  can't properly receive this message, just put us in the
347
        //  a state where we are looking for a new message
348
        i2c_data_p->operating_state = I2C_IDLE;
349
        overrun_error = 1;
350
        i2c_data_p->return_status = I2C_ERR_OVERRUN;
351
    }
352
 
353
    // Read SPPxBUF if it is full
354
    if (SSP1STATbits.BF == 1) {
355
        received_data = SSP1BUF;
356
        data_read_from_buffer = 1;
357
    }
358
 
359
    if (!overrun_error) {
360
        switch (i2c_data_p->operating_state) {
361
            case I2C_IDLE:
362
            {
277 Kevin 363
//                // Ignore anything except a start
364
//                if (SSP1STATbits.S == 1) {
365
//                    i2c_data_p->buffer_in_len_tmp = 0;
366
//                    i2c_data_p->operating_state = I2C_STARTED;
367
//                }
368
//                break;
369
//            }
370
//            case I2C_STARTED:
371
//            {
260 Kevin 372
                // In this case, we expect either an address or a stop bit
373
                if (SSP1STATbits.P == 1) {
374
                    // Return to idle mode
375
                    i2c_data_p->operating_state = I2C_IDLE;
376
                } else if (data_read_from_buffer) {
277 Kevin 377
                    i2c_data_p->buffer_in_len_tmp = 0;
260 Kevin 378
                    if (SSP1STATbits.D_nA == 0) {
379
                        // Address received
380
                        if (SSP1STATbits.R_nW == 0) {
381
                            // Slave write mode
382
                            i2c_data_p->operating_state = I2C_RCV_DATA;
383
                        } else {
384
                            // Slave read mode
385
                            i2c_data_p->operating_state = I2C_SEND_DATA;
386
                            // Process the first byte immediatly if sending data
387
                            goto send;
388
                        }
389
                    } else {
390
                        i2c_data_p->operating_state = I2C_IDLE;
277 Kevin 391
                        i2c_data_p->return_status = I2C_ERR_NOADDR;
260 Kevin 392
                    }
393
                }
394
                break;
395
            }
396
            send:
397
            case I2C_SEND_DATA:
398
            {
399
                if (!i2c_data_p->slave_sending_data) {
400
                    // If we are not currently sending data, figure out what to reply with
401
                    if (I2C1_Process_Receive(i2c_data_p->slave_in_last_byte)) {
402
                        // Data exists to be returned, send first byte
403
                        SSP1BUF = i2c_data_p->buffer_out[0];
404
                        i2c_data_p->buffer_out_ind = 1;
405
                        i2c_data_p->slave_sending_data = 1;
406
                        data_written_to_buffer = 1;
407
                    } else {
408
                        // Unknown request
409
                        i2c_data_p->slave_sending_data = 0;
410
                        i2c_data_p->operating_state = I2C_IDLE;
411
                    }
412
                } else {
413
                    // Sending remaining data back to master
414
                    if (i2c_data_p->buffer_out_ind < i2c_data_p->buffer_out_len) {
415
                        SSP1BUF = i2c_data_p->buffer_out[i2c_data_p->buffer_out_ind];
416
                        i2c_data_p->buffer_out_ind++;
417
                        data_written_to_buffer = 1;
418
                    } else {
419
                        // Nothing left to send
420
                        i2c_data_p->slave_sending_data = 0;
421
                        i2c_data_p->operating_state = I2C_IDLE;
422
                    }
423
                }
424
                break;
425
            }
426
            case I2C_RCV_DATA:
427
            {
428
                // We expect either data or a stop bit or a (if a restart, an addr)
429
                if (SSP1STATbits.P == 1) {
430
                    // Stop bit detected, we need to check to see if we also read data
431
                    if (data_read_from_buffer) {
432
                        if (SSP1STATbits.D_nA == 1) {
433
                            // Data received with stop bit
434
                            // TODO: Handle I2C buffer overflow
435
                            i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = received_data;
436
                            if (i2c_data_p->buffer_in_write_ind == MAXI2C1BUF-1) {
437
                                i2c_data_p->buffer_in_write_ind = 0;
438
                            } else {
439
                                i2c_data_p->buffer_in_write_ind++;
440
                            }
441
                            i2c_data_p->buffer_in_len_tmp++;
442
                            // Save the last byte received
443
                            i2c_data_p->slave_in_last_byte = received_data;
444
                            i2c_data_p->return_status = I2C_DATA_AVAL;
445
                        } else {
446
                            i2c_data_p->operating_state = I2C_IDLE;
447
                            i2c_data_p->return_status = I2C_ERR_NODATA;
448
                        }
449
                    }
450
                    i2c_data_p->buffer_in_len += i2c_data_p->buffer_in_len_tmp;
451
                    i2c_data_p->operating_state = I2C_IDLE;
452
                } else if (data_read_from_buffer) {
453
                    if (SSP1STATbits.D_nA == 1) {
454
                        // Data received
455
                        i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = received_data;
456
                        if (i2c_data_p->buffer_in_write_ind == MAXI2C1BUF-1) {
457
                            i2c_data_p->buffer_in_write_ind = 0;
458
                        } else {
459
                            i2c_data_p->buffer_in_write_ind++;
460
                        }
461
                        i2c_data_p->buffer_in_len_tmp++;
462
                        // Save the last byte received
463
                        i2c_data_p->slave_in_last_byte = received_data;
464
                        i2c_data_p->return_status = I2C_DATA_AVAL;
465
                    } else {
466
                        // Restart bit detected
467
                        if (SSP1STATbits.R_nW == 1) {
468
                            i2c_data_p->buffer_in_len += i2c_data_p->buffer_in_len_tmp;
469
                            i2c_data_p->operating_state = I2C_SEND_DATA;
470
                            // Process the first byte immediatly if sending data
471
                            goto send;
472
                        } else {
473
                            // Bad to recv an address again, we aren't ready
474
                            i2c_data_p->operating_state = I2C_IDLE;
475
                            i2c_data_p->return_status = I2C_ERR_NODATA;
476
                        }
477
                    }
478
                }
479
                break;
480
            }
481
        }
482
    }
483
 
484
    // Release the clock stretching bit (if we should)
485
    if (data_read_from_buffer || data_written_to_buffer) {
486
        // Release the clock
487
        if (SSP1CON1bits.CKP == 0) {
488
            SSP1CON1bits.CKP = 1;
489
        }
490
    }
491
}
492
 
493
/* Returns 0 if I2C module is currently busy, otherwise returns status code */
494
uint8_t I2C1_Get_Status() {
495
    if (i2c_data_p->operating_mode == I2C_MODE_MASTER) {
496
        if (i2c_data_p->master_status != I2C_MASTER_IDLE || i2c_data_p->buffer_in_len == 0) {
497
            return 0;
498
        } else {
499
            return i2c_data_p->return_status;
500
        }
501
    } else {
502
        if (i2c_data_p->operating_state != I2C_IDLE || i2c_data_p->buffer_in_len == 0) {
503
            return 0;
504
        } else {
505
            return i2c_data_p->return_status;
506
        }
507
    }
508
}
509
 
510
uint8_t I2C1_Buffer_Len() {
511
    return i2c_data_p->buffer_in_len;
512
}
513
 
514
/* Returns 0 if I2C module is currently busy, otherwise returns buffer length */
515
uint8_t I2C1_Read_Buffer(uint8_t *buffer) {
516
    uint8_t i = 0;
517
    while (i2c_data_p->buffer_in_len != 0) {
518
        buffer[i] = i2c_data_p->buffer_in[i2c_data_p->buffer_in_read_ind];
519
        i++;
520
        if (i2c_data_p->buffer_in_read_ind == MAXI2C1BUF-1) {
521
            i2c_data_p->buffer_in_read_ind = 0;
522
        } else {
523
            i2c_data_p->buffer_in_read_ind++;
524
        }
525
        i2c_data_p->buffer_in_len--;
526
    }
527
    return i;
528
}
529
 
530
/* Put data to be returned here */
531
uint8_t I2C1_Process_Receive(uint8_t c) {
532
    uint8_t ret = 0;
533
    switch (c) {
534
        case CMD_QUERY_BTN:
273 Kevin 535
            i2c_data_p->buffer_out[0] = btns.w;
536
            i2c_data_p->buffer_out_len = 1;
537
            ret = 1;
260 Kevin 538
            break;
273 Kevin 539
        default:
540
            break;
260 Kevin 541
    }
273 Kevin 542
 
260 Kevin 543
    return ret;
544
}