12 #include "sio2man_hook.h"
14 #include "spi_sdcard_driver.h"
16 #include "module_debug.h"
18 IRX_ID(
"mx4sio", 1, 2);
24 static int sd_detect_thread_id = -1;
26 static uint8_t sio2_current_baud = SIO2_BAUD_DIV_SLOW;
27 static uint32_t sio2_save_crtl;
30 int mx_sio2_dma_isr_rx(
void *arg)
33 int eflags = EF_SIO2_INTR_REVERSE;
36 inl_sio2_stat_set(inl_sio2_stat_get());
39 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
42 #ifdef CONFIG_USE_CRC16
44 cmd.crc[cmd.sectors_transferred] = reverse_byte_LUT8[inl_sio2_data_in()] << 8;
45 cmd.crc[cmd.sectors_transferred] |= reverse_byte_LUT8[inl_sio2_data_in()];
54 if (inl_sio2_data_in() != 0xFE) {
55 cmd.response = mx_sio2_wait_equal(0xFE, READ_TOKEN_TIMEOUT);
57 cmd.response = SPISD_RESULT_OK;
60 cmd.sectors_transferred++;
62 if (cmd.sectors_transferred < cmd.sector_count && cmd.abort == 0) {
64 if (cmd.response == SPISD_RESULT_OK) {
66 mx_sio2_start_rx_dma(&cmd.buffer[cmd.sectors_transferred * SECTOR_SIZE]);
68 iSetEventFlag(ef, eflags);
73 cmd.abort = CMD_ABORT_NO_READ_TOKEN;
77 eflags |= EF_SIO2_INTR_COMPLETE;
78 iSetEventFlag(ef, eflags);
84 int mx_sio2_dma_isr_tx(
void *arg)
90 inl_sio2_stat_set(inl_sio2_stat_get());
97 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
101 mx_sio2_write_dummy();
102 mx_sio2_write_dummy();
104 cmd.sectors_transferred++;
110 cmd.response = mx_sio2_wait_equal_masked(0x5, 0x1F, 10);
111 if (cmd.response != 0x5) {
112 M_DEBUG(
"ERROR: write data rejected, token 0x%x\n", cmd.response);
117 cmd.response = mx_sio2_wait_equal(0xFF, 0x80000);
118 if (cmd.response != SPISD_RESULT_OK) {
119 M_DEBUG(
"ERROR: failed to finish programming\n");
123 if (cmd.sectors_transferred < cmd.sector_count && cmd.abort == 0) {
125 mx_sio2_write_byte(0xFC);
127 mx_sio2_start_tx_dma(&cmd.buffer[cmd.sectors_transferred * SECTOR_SIZE]);
133 eflags = EF_SIO2_INTR_COMPLETE;
134 iSetEventFlag(ef, eflags);
141 for (uint8_t i = 0; i < 4; i++) {
142 inl_sio2_portN_ctrl1_set(i, td->port_ctrl1[i]);
143 inl_sio2_portN_ctrl2_set(i, td->port_ctrl2[i]);
149 for (uint8_t i = 0; i < 4; i++) {
150 td->port_ctrl1[i] = 0;
151 td->port_ctrl2[i] = 0;
154 td->port_ctrl1[PORT_NR] =
155 PCTRL0_ATT_LOW_PER(0x5) |
156 PCTRL0_ATT_MIN_HIGH_PER(0x5) |
157 PCTRL0_BAUD0_DIV(0x78) |
158 PCTRL0_BAUD1_DIV(sio2_current_baud);
160 td->port_ctrl2[PORT_NR] =
161 PCTRL1_ACK_TIMEOUT_PER(0x12C) |
162 PCTRL1_INTER_BYTE_PER(0x0) |
164 PCTRL1_IF_MODE_SPI_DIFF(0x0);
167 void mx_sio2_lock(uint8_t intr_type)
172 sio2man_hook_sio2_lock();
175 sio2_save_crtl = inl_sio2_ctrl_get();
178 mx_sio2_init_ports(&global_td);
183 if (intr_type == INTR_RX) {
188 if (intr_type == INTR_TX) {
196 void mx_sio2_unlock(uint8_t intr_type)
204 if (intr_type == INTR_RX)
207 if (intr_type == INTR_TX)
213 inl_sio2_ctrl_set(sio2_save_crtl | 0xc);
216 sio2man_hook_sio2_unlock();
219 void mx_sio2_set_baud(uint8_t baud)
221 sio2_current_baud = baud;
223 mx_sio2_init_td(&global_td);
224 mx_sio2_init_ports(&global_td);
227 uint8_t mx_sio2_write_byte(uint8_t
byte)
230 inl_sio2_ctrl_set(0x0bc);
233 TR_CTRL_PORT_NR(PORT_NR) |
235 TR_CTRL_TX_MODE_PIO_DMA(0) |
236 TR_CTRL_RX_MODE_PIO_DMA(0) |
237 TR_CTRL_NORMAL_TR(1) |
238 TR_CTRL_SPECIAL_TR(0) |
239 TR_CTRL_BAUD_DIV(1) |
240 TR_CTRL_WAIT_ACK_FOREVER(0) |
241 TR_CTRL_TX_DATA_SZ(1) |
242 TR_CTRL_RX_DATA_SZ(1));
243 inl_sio2_regN_set(1, 0);
246 inl_sio2_data_out(reverse_byte_LUT8[
byte]);
249 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
252 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
256 uint8_t rx = reverse_byte_LUT8[inl_sio2_data_in()];
257 M_DEBUG(
"W:0x%x, R:0x%x\n",
byte, rx);
262 return reverse_byte_LUT8[inl_sio2_data_in()];
265 uint8_t mx_sio2_write_dummy(
void)
268 inl_sio2_ctrl_set(0x0bc);
272 TR_CTRL_PORT_NR(PORT_NR) |
274 TR_CTRL_TX_MODE_PIO_DMA(0) |
275 TR_CTRL_RX_MODE_PIO_DMA(0) |
276 TR_CTRL_NORMAL_TR(1) |
277 TR_CTRL_SPECIAL_TR(0) |
278 TR_CTRL_BAUD_DIV(1) |
279 TR_CTRL_WAIT_ACK_FOREVER(0) |
280 TR_CTRL_TX_DATA_SZ(1) |
281 TR_CTRL_RX_DATA_SZ(1));
282 inl_sio2_regN_set(1, 0);
285 inl_sio2_data_out(0xFF);
288 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
291 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
295 uint8_t rx = reverse_byte_LUT8[inl_sio2_data_in()];
296 M_DEBUG(
"R:0x%x\n", rx);
301 return reverse_byte_LUT8[inl_sio2_data_in()];
305 uint8_t mx_sio2_wait_equal(uint8_t value, uint32_t
count)
307 uint8_t exp_byte = reverse_byte_LUT8[value];
310 while (
count > 0 && in_byte != exp_byte) {
312 inl_sio2_ctrl_set(0x0bc);
315 TR_CTRL_PORT_NR(PORT_NR) |
317 TR_CTRL_TX_MODE_PIO_DMA(0) |
318 TR_CTRL_RX_MODE_PIO_DMA(0) |
319 TR_CTRL_NORMAL_TR(1) |
320 TR_CTRL_SPECIAL_TR(0) |
321 TR_CTRL_BAUD_DIV(1) |
322 TR_CTRL_WAIT_ACK_FOREVER(0) |
323 TR_CTRL_TX_DATA_SZ(0) |
324 TR_CTRL_RX_DATA_SZ(1));
325 inl_sio2_regN_set(1, 0);
328 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
331 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
334 in_byte = inl_sio2_data_in();
337 M_DEBUG(
"WE: 0x%x, EX: 0x%x\n", reverse_byte_LUT8[in_byte], value);
343 return (exp_byte != in_byte) ? SPISD_RESULT_ERROR : SPISD_RESULT_OK;
346 uint8_t mx_sio2_wait_not_equal(uint8_t value, uint32_t
count)
348 uint8_t exp_byte = reverse_byte_LUT8[value];
349 uint8_t in_byte = exp_byte;
351 while (
count > 0 && in_byte == exp_byte) {
353 inl_sio2_ctrl_set(0x0bc);
357 TR_CTRL_PORT_NR(PORT_NR) |
359 TR_CTRL_TX_MODE_PIO_DMA(0) |
360 TR_CTRL_RX_MODE_PIO_DMA(0) |
361 TR_CTRL_NORMAL_TR(1) |
362 TR_CTRL_SPECIAL_TR(0) |
363 TR_CTRL_BAUD_DIV(1) |
364 TR_CTRL_WAIT_ACK_FOREVER(0) |
365 TR_CTRL_TX_DATA_SZ(0) |
366 TR_CTRL_RX_DATA_SZ(1));
367 inl_sio2_regN_set(1, 0);
370 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
373 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
377 in_byte = inl_sio2_data_in();
380 M_DEBUG(
"WNE: 0x%x, EX: !0x%x\n", reverse_byte_LUT8[in_byte], value);
385 return reverse_byte_LUT8[in_byte];
388 uint8_t mx_sio2_wait_equal_masked(uint8_t value, uint8_t mask, uint32_t
count)
390 uint8_t exp_byte = reverse_byte_LUT8[value];
391 uint8_t rev_mask = reverse_byte_LUT8[mask];
394 while (
count > 0 && in_byte != exp_byte) {
396 inl_sio2_ctrl_set(0x0bc);
399 TR_CTRL_PORT_NR(PORT_NR) |
401 TR_CTRL_TX_MODE_PIO_DMA(0) |
402 TR_CTRL_RX_MODE_PIO_DMA(0) |
403 TR_CTRL_NORMAL_TR(1) |
404 TR_CTRL_SPECIAL_TR(0) |
405 TR_CTRL_BAUD_DIV(1) |
406 TR_CTRL_WAIT_ACK_FOREVER(0) |
407 TR_CTRL_TX_DATA_SZ(0) |
408 TR_CTRL_RX_DATA_SZ(1));
409 inl_sio2_regN_set(1, 0);
412 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
415 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
418 in_byte = inl_sio2_data_in();
421 M_DEBUG(
"WEM: 0x%x M:\n", reverse_byte_LUT8[in_byte], reverse_byte_LUT8[in_byte & rev_mask]);
423 in_byte = in_byte & rev_mask;
428 return reverse_byte_LUT8[in_byte];
431 void mx_sio2_rx_pio(uint8_t *buffer, uint32_t size)
433 uint32_t transfer_size;
435 uint8_t *buffer_start = buffer;
439 transfer_size = size > SIO2_MAX_TRANSFER_SIZE ? SIO2_MAX_TRANSFER_SIZE : size;
442 inl_sio2_ctrl_set(0x0bc);
446 TR_CTRL_PORT_NR(PORT_NR) |
448 TR_CTRL_TX_MODE_PIO_DMA(0) |
449 TR_CTRL_RX_MODE_PIO_DMA(0) |
450 TR_CTRL_NORMAL_TR(1) |
451 TR_CTRL_SPECIAL_TR(0) |
452 TR_CTRL_BAUD_DIV(1) |
453 TR_CTRL_WAIT_ACK_FOREVER(0) |
454 TR_CTRL_TX_DATA_SZ(0) |
455 TR_CTRL_RX_DATA_SZ(transfer_size));
456 inl_sio2_regN_set(1, 0);
459 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
462 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
466 for (
int i = 0; i < transfer_size; i++) {
467 *buffer++ = reverse_byte_LUT8[inl_sio2_data_in()];
471 for (
int i = 0; i < transfer_size; i++) {
472 M_DEBUG(
"W:0xFF, R:0x%x\n", buffer_start[i]);
476 size -= transfer_size;
480 void mx_sio2_tx_pio(uint8_t *buffer, uint32_t size)
482 uint32_t transfer_size;
485 uint8_t *buffer_start = buffer;
490 transfer_size = size > SIO2_MAX_TRANSFER_SIZE ? SIO2_MAX_TRANSFER_SIZE : size;
493 inl_sio2_ctrl_set(0x0bc);
497 TR_CTRL_PORT_NR(PORT_NR) |
499 TR_CTRL_TX_MODE_PIO_DMA(0) |
500 TR_CTRL_RX_MODE_PIO_DMA(0) |
501 TR_CTRL_NORMAL_TR(1) |
502 TR_CTRL_SPECIAL_TR(0) |
503 TR_CTRL_BAUD_DIV(1) |
504 TR_CTRL_WAIT_ACK_FOREVER(0) |
505 TR_CTRL_TX_DATA_SZ(transfer_size) |
507 TR_CTRL_RX_DATA_SZ(transfer_size));
509 TR_CTRL_RX_DATA_SZ(0));
512 inl_sio2_regN_set(1, 0);
515 for (
int i = 0; i < transfer_size; i++) {
516 inl_sio2_data_out(reverse_byte_LUT8[*buffer++]);
520 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
523 while ((inl_sio2_stat6c_get() & (1 << 12)) == 0)
527 for (
int i = 0; i < transfer_size; i++) {
528 M_DEBUG(
"W:0x%x, R:0x%x\n", buffer_start[i], reverse_byte_LUT8[inl_sio2_data_in()]);
531 size -= transfer_size;
535 void mx_sio2_start_rx_dma(uint8_t *buffer)
538 const uint32_t tr_ctrl_dma =
540 TR_CTRL_TX_MODE_PIO_DMA(0) |
541 TR_CTRL_RX_MODE_PIO_DMA(1) |
542 TR_CTRL_NORMAL_TR(1) |
543 TR_CTRL_SPECIAL_TR(0) |
544 TR_CTRL_TX_DATA_SZ(0) |
545 TR_CTRL_RX_DATA_SZ(0x100) |
546 TR_CTRL_BAUD_DIV(1) |
547 TR_CTRL_WAIT_ACK_FOREVER(0);
550 const uint32_t tr_ctrl_pio =
552 TR_CTRL_TX_MODE_PIO_DMA(0) |
553 TR_CTRL_RX_MODE_PIO_DMA(0) |
554 TR_CTRL_NORMAL_TR(1) |
555 TR_CTRL_SPECIAL_TR(0) |
556 TR_CTRL_TX_DATA_SZ(0) |
557 TR_CTRL_RX_DATA_SZ(3) |
558 TR_CTRL_BAUD_DIV(1) |
559 TR_CTRL_WAIT_ACK_FOREVER(0);
562 inl_sio2_ctrl_set(0x0bc);
565 inl_sio2_regN_set(0, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
566 inl_sio2_regN_set(1, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
567 inl_sio2_regN_set(2, tr_ctrl_pio | TR_CTRL_PORT_NR(PORT_NR));
568 inl_sio2_regN_set(3, 0);
571 sceSetSliceDMA(IOP_DMAC_SIO2out, buffer, 0x100 >> 2, 2, DMAC_TO_MEM);
572 sceStartDMA(IOP_DMAC_SIO2out);
575 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
578 void mx_sio2_start_tx_dma(uint8_t *buffer)
581 const uint32_t tr_ctrl_dma =
583 TR_CTRL_TX_MODE_PIO_DMA(1) |
584 TR_CTRL_RX_MODE_PIO_DMA(0) |
585 TR_CTRL_NORMAL_TR(1) |
586 TR_CTRL_SPECIAL_TR(0) |
587 TR_CTRL_TX_DATA_SZ(0x100) |
588 TR_CTRL_RX_DATA_SZ(0) |
589 TR_CTRL_BAUD_DIV(1) |
590 TR_CTRL_WAIT_ACK_FOREVER(0);
593 inl_sio2_ctrl_set(0x0bc);
596 inl_sio2_regN_set(0, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
597 inl_sio2_regN_set(1, tr_ctrl_dma | TR_CTRL_PORT_NR(PORT_NR));
598 inl_sio2_regN_set(2, 0);
599 inl_sio2_regN_set(3, 0);
602 sceSetSliceDMA(IOP_DMAC_SIO2in, buffer, 0x100 >> 2, 2, DMAC_FROM_MEM);
603 sceStartDMA(IOP_DMAC_SIO2in);
606 inl_sio2_ctrl_set(inl_sio2_ctrl_get() | 1);
609 static void sd_detect()
613 mx_sio2_lock(INTR_NONE);
615 if (sdcard.initialized == 0) {
616 M_PRINTF(
"Trying to init card\n");
618 if (spisd_init_card() == SPISD_RESULT_OK) {
621 if (spisd_get_card_info() == SPISD_RESULT_OK) {
623 sdcard.initialized = 1;
628 results = spisd_read_status_register();
630 if (results != 0x0) {
632 results = spisd_recover();
634 if ((sdcard.initialized == 1) && (results != SPISD_RESULT_OK)) {
636 M_DEBUG(
"Recovery failed, disconnecting from bdm.\n");
637 bdm_disconnect_bd(&bd);
638 sdcard.initialized = 0;
643 mx_sio2_unlock(INTR_NONE);
646 static void sd_detect_thread(
void *arg)
650 M_PRINTF(
"card detection thread running\n");
653 DelayThread(1000 * 1000);
656 if (sdcard.used == 0)
665 #pragma GCC push_options
666 #pragma GCC optimize("-O3")
667 inline void reverse_buffer(uint32_t *buffer, uint32_t
count)
669 const uint32_t mask0F = 0x0F0F0F0F;
670 const uint32_t maskF0 = 0xF0F0F0F0;
671 const uint32_t mask33 = 0x33333333;
672 const uint32_t maskCC = 0xCCCCCCCC;
673 const uint32_t mask55 = 0x55555555;
674 const uint32_t maskAA = 0xAAAAAAAA;
678 for (
int i = 0; i <
count; i++) {
680 n = ((n & maskF0) >> 4) | ((n & mask0F) << 4);
681 n = ((n & maskCC) >> 2) | ((n & mask33) << 2);
682 n = ((n & maskAA) >> 1) | ((n & mask55) << 1);
687 #pragma GCC pop_options
690 const uint8_t reverse_byte_LUT8[256] = {
691 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
692 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
693 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
694 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
695 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
696 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
697 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
698 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
699 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
700 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
701 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
702 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
703 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
704 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
705 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
706 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
707 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
708 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
709 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
710 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
711 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
712 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
713 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
714 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
715 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
716 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
717 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
718 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
719 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
720 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
721 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
722 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff};
725 static int module_start(
int argc,
char *argv[],
void *startaddr,
ModuleInfo_t *mi)
734 M_PRINTF(
"Starting module\n");
735 for (i = 0; i < argc; i++)
736 M_PRINTF(
" - argv[%d] = %s\n", i, argv[i]);
744 mx_sio2_init_td(&global_td);
750 sio2_event_flag = CreateEventFlag(&
event);
751 if (sio2_event_flag < 0) {
752 M_PRINTF(
"ERROR: CreateEventFlag returned %d\n", sio2_event_flag);
756 rv = sio2man_hook_init();
758 M_PRINTF(
"ERROR: sio2man_hook_init returned %d\n", rv);
763 sceSetDMAPriority(IOP_DMAC_SIO2in, 3);
764 sceSetDMAPriority(IOP_DMAC_SIO2out, 3);
765 sceEnableDMAChannel(IOP_DMAC_SIO2in);
766 sceEnableDMAChannel(IOP_DMAC_SIO2out);
772 mx_sio2_lock(INTR_NONE);
773 mx_sio2_rx_pio((
void *)&rv, 4);
774 mx_sio2_unlock(INTR_NONE);
778 thread.thread = sd_detect_thread;
780 thread.priority = USER_LOWEST_PRIORITY;
781 thread.stacksize = 0x1000;
782 rv = sd_detect_thread_id = CreateThread(&
thread);
784 M_PRINTF(
"ERROR: CreateThread returned %d\n", rv);
789 rv = StartThread(sd_detect_thread_id, NULL);
791 M_PRINTF(
"ERROR: StartThread returned %d\n", rv);
797 if (mi && ((mi->
newflags & 2) != 0))
799 return MODULE_RESIDENT_END;
802 DeleteThread(sd_detect_thread_id);
804 sio2man_hook_deinit();
806 DeleteEventFlag(sio2_event_flag);
808 return MODULE_NO_RESIDENT_END;
811 static int module_stop(
int argc,
char *argv[],
void *startaddr,
ModuleInfo_t *mi)
816 M_PRINTF(
"Stopping module\n");
817 for (i = 0; i < argc; i++)
818 M_PRINTF(
" - argv[%d] = %s\n", i, argv[i]);
826 DeleteThread(sd_detect_thread_id);
827 sio2man_hook_deinit();
828 DeleteEventFlag(sio2_event_flag);
830 return MODULE_NO_RESIDENT_END;
833 int _start(
int argc,
char *argv[],
void *startaddr,
ModuleInfo_t *mi)
835 M_PRINTF(
"MX4SIO v1.2\n");
838 return module_start(argc, argv, startaddr, mi);
840 return module_stop(-argc, argv, startaddr, mi);