www.pudn.com > sdio-2.6.18-full.rar > gps_os.c
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @file: gps_os.c @abstract: OS dependent GPS class SDIO function driver #notes: @notice: Copyright (c), 2004-2005 Atheros Communications, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Portions o this code were developed with information supplied from the * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: * * The following conditions apply to the release of the SD simplified specification (“Simplified * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified * Specification may require a license from the SD Card Association or other third parties. * Disclaimers: * The information contained in the Simplified Specification is presented only as a standard * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for * any damages, any infringements of patents or other right of the SD Card Association or any third * parties, which may result from its use. No license is granted by implication, estoppel or otherwise * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall * be construed as an obligation by the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. * * * The initial developers of the original code are Seung Yi and Paul Lever * * sdio@atheros.com * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* debug level for this module*/ #define DBG_DECLARE 3; #include#include #include #include "gps.h" #include #include #include #include #include #define DESCRIPTION "SDIO GPS Function Driver" #define AUTHOR "Atheros Communications, Inc." /* debug print parameter */ module_param(debuglevel, int, 0644); MODULE_PARM_DESC(debuglevel, "debuglevel 0-7, controls debug prints"); int fixedbaud = 1; module_param(fixedbaud, int, 0644); MODULE_PARM_DESC(fixedbaud, "fixedbaud, if non-zero then no baud rate changes will be processed"); static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice); static void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice); static unsigned int gps_tx_empty(struct uart_port *port); static void gps_set_mctrl(struct uart_port *port, unsigned int mctrl); static unsigned int gps_get_mctrl(struct uart_port *port); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) static void gps_stop_tx(struct uart_port *port); static void gps_start_tx(struct uart_port *port); #else static void gps_stop_tx(struct uart_port *port, unsigned int tty_stop); static void gps_start_tx(struct uart_port *port, unsigned int tty_start); #endif static void gps_stop_rx(struct uart_port *port); static void gps_enable_ms(struct uart_port *port); static void gps_break_ctl(struct uart_port *port, int break_state); static int gps_startup(struct uart_port *port); static void gps_shutdown(struct uart_port *port); static void gps_set_termios(struct uart_port *port, struct termios *termios, struct termios *old); static void gps_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); static const char *gps_type(struct uart_port *port); static void gps_release_port(struct uart_port *port); static int gps_request_port(struct uart_port *port); static void gps_config_port(struct uart_port *port, int flags); static int gps_verify_port(struct uart_port *port, struct serial_struct *ser); /* devices we support, null terminated */ #define SDIO_GPS_CLASS 0x04 static SD_PNP_INFO Ids[] = { {.SDIO_FunctionClass = SDIO_GPS_CLASS}, /* SDIO-GPS SDIO standard interface code */ {} }; static struct uart_ops sops = { .tx_empty = gps_tx_empty, .set_mctrl = gps_set_mctrl, .get_mctrl = gps_get_mctrl, .stop_tx = gps_stop_tx, .start_tx = gps_start_tx, .stop_rx = gps_stop_rx, .enable_ms = gps_enable_ms, .break_ctl = gps_break_ctl, .startup = gps_startup, .shutdown = gps_shutdown, .set_termios = gps_set_termios, .pm = gps_pm, .type = gps_type, .release_port = gps_release_port, .request_port = gps_request_port, .config_port = gps_config_port, .verify_port = gps_verify_port, }; /* the driver context data */ static SDGPS_DRIVER_CONTEXT GpsContext = { .Function.pName = "sdio_gps", .Function.Version = CT_SDIO_STACK_VERSION_CODE, .Function.MaxDevices = 1, .Function.NumDevices = 0, .Function.pIds = Ids, .Function.pProbe = Probe, .Function.pRemove = Remove, .Function.pSuspend = NULL, .Function.pResume = NULL, .Function.pWake = NULL, .Function.pContext = &GpsContext, .GpsDevice.Port.type = PORT_16550, .GpsDevice.Port.uartclk = GPS_MAX_BAUD_RATE*16, .GpsDevice.Port.fifosize = GPS_FIFO_SIZE, .GpsDevice.Port.line = 0, .GpsDevice.Port.ops = &sops, }; static struct uart_driver gps_uart = { .owner = THIS_MODULE, .driver_name = "ttyGPS", /* not sure if this is proper naming convention */ .dev_name = "ttyGPS", #ifdef CONFIG_DEVFS .devfs_name = "ttygps", #endif .major = 0, /*TTY_MAJOR,*/ .minor = 0, /*64,*/ .nr = 1, }; /* * Probe - a device potentially for us */ static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { PSDGPS_DRIVER_CONTEXT pFunctionContext = (PSDGPS_DRIVER_CONTEXT)pFunction->pContext; SYSTEM_STATUS err = 0; SDIO_STATUS status; DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - enter\n")); /* make sure this is a device we can handle */ if (pDevice->pId[0].SDIO_FunctionClass == pFunctionContext->Function.pIds[0].SDIO_FunctionClass) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - card matched (0x%X/0x%X/0x%X)\n", pDevice->pId[0].SDIO_ManufacturerID, pDevice->pId[0].SDIO_ManufacturerCode, pDevice->pId[0].SDIO_FunctionNo)); /* connect to the serial port driver */ pFunctionContext->pSDDevice = pDevice; pFunctionContext->GpsDevice.pSDDevice = pDevice; pFunctionContext->GpsDevice.HwReady = FALSE; if (!SDIO_SUCCESS((status = GpsInitialize(&pFunctionContext->GpsDevice)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Probe - could not initialize, %d\n", status)); return FALSE; } /* the port structure must be reset each time it is re-used */ ZERO_OBJECT(pFunctionContext->GpsDevice.Port); pFunctionContext->GpsDevice.Port.type = PORT_16550; pFunctionContext->GpsDevice.Port.uartclk = GPS_MAX_BAUD_RATE*16; pFunctionContext->GpsDevice.Port.fifosize = GPS_FIFO_SIZE; pFunctionContext->GpsDevice.Port.line = 0; pFunctionContext->GpsDevice.Port.ops = &sops; pFunctionContext->GpsDevice.Port.dev = SD_GET_OS_DEVICE(pDevice); pFunctionContext->GpsDevice.Port.uartclk = pFunctionContext->GpsDevice.UartMaxBaud * 16; //?? snprintf(gps_uart->tty_driver.devfs_name,sizeof(gps_uart->tty_driver.devfs_name), "sdgps_%s", //?? (pFunctionContext->GpsDevice.Port.dev->bus_id); //?? /* remove any colons */ //?? ReplaceChar(gps_uart->devfs_name, ':', '_'); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - adding bus_id: %s, driver name: %s\n", pFunctionContext->GpsDevice.Port.dev->bus_id, pFunctionContext->GpsDevice.Port.dev->driver->name)); err = uart_add_one_port(&gps_uart, &pFunctionContext->GpsDevice.Port); if (err < 0) { DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Probe - could not add uart port, %d\n", err)); return FALSE; } return TRUE; } else { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Probe - not our card (0x%X/0x%X/0x%X)\n", pDevice->pId[0].SDIO_ManufacturerID, pDevice->pId[0].SDIO_ManufacturerCode, pDevice->pId[0].SDIO_FunctionNo)); return FALSE; } } /* * Remove - our device is being removed */ void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) { PSDGPS_DRIVER_CONTEXT pFunctionContext = (PSDGPS_DRIVER_CONTEXT)pFunction->pContext; SYSTEM_STATUS err; DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: Remove - enter\n")); GpsDeinitialize(&pFunctionContext->GpsDevice); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: Remove - calling uart_remove_one_port\n")); err = uart_remove_one_port(&gps_uart, &pFunctionContext->GpsDevice.Port); if (err < 0) { DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: Remove - could not remove uart port, %d\n", err)); } pFunctionContext->pSDDevice = NULL; DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: Remove - exit\n")); } /* * gps_tx_empty - transmitter fifo and shifter is empty */ static unsigned int gps_tx_empty(struct uart_port *port) { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); UINT8 data; DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_tx_empty - enter\n")); ReadRegister(pDevice, UART_LINE_STATUS_REG, &data); return (data & UART_LSR_TEMT)? TIOCSER_TEMT : 0; } /* * gps_set_mctrl - set modem control line */ static void gps_set_mctrl(struct uart_port *port, unsigned int mctrl) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_set_mctrl - enter\n")); return; /* fixed values */ } /* * gps_get_mctrl - get modem control line */ static unsigned int gps_get_mctrl(struct uart_port *port) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_get_mctrl - enter\n")); return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; /* fixed */ } /* * gps_stop_tx - stop output */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) static void gps_stop_tx(struct uart_port *port) #else static void gps_stop_tx(struct uart_port *port, unsigned int tty_stop) #endif { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_stop_tx - enter\n")); if (pDevice->InterruptEnable & UART_ETBEI) { /* called with local ints disabled if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { return; } */ pDevice->InterruptEnable &= ~UART_ETBEI; WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); /*SemaphorePost(&pDevice->DeviceSem);*/ } } /* * gps_start_tx - start output */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) static void gps_start_tx(struct uart_port *port) #else static void gps_start_tx(struct uart_port *port, unsigned int tty_start) #endif { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: gps_start_tx - enter\n")); if (!(pDevice->InterruptEnable & UART_ETBEI)) { /* called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { return; } */ pDevice->InterruptEnable |= UART_ETBEI; WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); /*SemaphorePost(&pDevice->DeviceSem);*/ } DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: gps_start_tx\n")); } /* * gps_stop_rx - stop output */ static void gps_stop_rx(struct uart_port *port) { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_stop_rx - enter\n")); /* called with local ints disabled if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { return; } */ pDevice->InterruptEnable &= ~UART_ELSI; WriteRegisterAsynch(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable, pDevice->pRequest); /*SemaphorePost(&pDevice->DeviceSem);*/ } /* * gps_enable_ms - enable modem status interrupts */ static void gps_enable_ms(struct uart_port *port) { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_enable_ms - enter\n")); /* called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { return; } */ pDevice->InterruptEnable |= UART_EDSSI; WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); /*SemaphorePost(&pDevice->DeviceSem);*/ } /* * gps_break_ctl - enable/disable breaks */ static void gps_break_ctl(struct uart_port *port, int break_state) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_break_ctl - enter\n")); } /* * gps_startup - initialization */ static int gps_startup(struct uart_port *port) { UINT8 temp; PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_startup - enter\n")); //?? if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { //?? return -EINTR; //?? } /* read registers to clear status */ ReadRegister(pDevice, UART_LINE_STATUS_REG, &temp); ReadRegister(pDevice, UART_RECEIVE_REG, &temp); ReadRegister(pDevice, UART_INT_IDENT_REG, &temp); ReadRegister(pDevice, UART_MODEM_STATUS_REG, &temp); WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE | UART_FIFO_RCV_RESET); WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE); WriteRegister(pDevice, UART_LINE_CNTRL_REG, (UART_ONE_STOP | UART_NO_PARITY | UART_DATA_8_BITS)); pDevice->InterruptEnable = UART_ERBFI | UART_ELSI; WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); //?? SemaphorePost(&pDevice->DeviceSem); return 0; } /* * gps_shutdown - */ static void gps_shutdown(struct uart_port *port) { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); DBG_PRINT(SDDBG_TRACE, ("+SDIO GPS Function: gps_shutdown - enter\n")); //??if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { //?? return; //??} pDevice->InterruptEnable = 0; WriteRegister(pDevice, UART_INT_ENABLE_REG, pDevice->InterruptEnable); //??SemaphorePost(&pDevice->DeviceSem); DBG_PRINT(SDDBG_TRACE, ("-SDIO GPS Function: gps_shutdown\n")); } /* * gps_set_termios - set data parameters */ static void gps_set_termios(struct uart_port *port, struct termios *termios, struct termios *old) { PSDGPS_DEVICE pDevice = CONTAINING_STRUCT(port, SDGPS_DEVICE, Port); unsigned char cval = 0; unsigned int baudrate; DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_set_termios - enter\n")); port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;//??? switch (termios->c_cflag & CSIZE) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; default: case CS8: cval = 0x03; break; } cval |= (termios->c_cflag & PARENB)? UART_PARITY_ENABLE : 0; cval |= (termios->c_cflag & CSTOPB)? UART_NUM_STOP_BITS : 0; cval |= (!(termios->c_cflag & PARODD))? UART_EVEN_PARITY_SELECT : 0; //?? WriteRegister(pDevice, UART_LINE_CNTRL_REG, cval); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: LCR 0x%X\n", cval)); /* get serial port to figure out the baud rate */ baudrate = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); DBG_ASSERT(baudrate != 0); if (fixedbaud == 0) { SetBaudRate(pDevice, baudrate); uart_update_timeout(port, termios->c_cflag, baudrate); } //??do something about this locking /* maybe called with local ints disabledif (!SDIO_SUCCESS(SemaphorePendInterruptable(&pDevice->DeviceSem))) { return; } */ //?? pDevice->InterruptEnable &= ~UART_EDSSI; //?? if (UART_ENABLE_MS(&port, termios->c_cflag)) { //?? pDevice->InterruptEnable |= UART_EDSSI; //?? } gps_set_mctrl(&pDevice->Port, pDevice->Port.mctrl); /* SemaphorePost(&pDevice->DeviceSem);*/ } /* * gps_pm - power management */ static void gps_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_pm - state: %d\n", state)); return; } /* * gps_type - retrieve type string */ static const char *gps_type(struct uart_port *port) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_type - enter\n")); return GPS_TYPE; } /* * gps_release_port - release resources */ static void gps_release_port(struct uart_port *port) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_release_port - enter\n")); return; } /* * gps_request_port - request resources */ static int gps_request_port(struct uart_port *port) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_request_port - enter\n")); return 0; } /* * gps_config_port - auto configuration */ static void gps_config_port(struct uart_port *port, int flags) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_config_port - enter\n")); return; } /* * gps_verify_port - verify configuration of port */ static int gps_verify_port(struct uart_port *port, struct serial_struct *ser) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: gps_verify_port - enter\n")); return 0; } /* * GpsReceive */ SDIO_STATUS GpsReceive(PSDGPS_DEVICE pDevice) { SDIO_STATUS status = SDIO_STATUS_SUCCESS; UINT8 statusReg; UINT8 inChar; UINT maxCount = 64; struct uart_port *port = &pDevice->Port; struct tty_struct *tty; tty = ((port == NULL) || (port->info == NULL)) ? NULL : port->info->tty; /* if we aren't ready, just clear out the incoming char */ if ((port == NULL) || (tty == NULL)) { ReadRegister(pDevice, UART_RECEIVE_REG, &inChar); WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE | UART_FIFO_RCV_RESET); WriteRegister(pDevice, UART_FIFO_CNTRL_REG, UART_DATA_8_BITS | UART_FIFO_ENABLE); DBG_PRINT(SDIO_GPS_TRACE_INT, ("+RX%X\n", (UINT)inChar)); return status; } for (status = ReadRegister(pDevice, UART_LINE_STATUS_REG, &statusReg); SDIO_SUCCESS(status) && (statusReg & UART_LSR_DR) && (maxCount-- > 0); status = ReadRegister(pDevice, UART_LINE_STATUS_REG, &statusReg)) { ReadRegister(pDevice, UART_RECEIVE_REG, &inChar); if (status & UART_LSR_OE) { tty_insert_flip_char(tty, 0, TTY_OVERRUN); DBG_PRINT(SDDBG_WARN, ("SDIO GPS Function: GpsReceive overrun\n")); break; } tty_insert_flip_char(tty, inChar, 0); udelay(1); } tty_flip_buffer_push(tty); return status; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) #define GPS_STOP_TX(p) gps_stop_tx((p)) #else #define GPS_STOP_TX(p) gps_stop_tx((p),0) #endif /* * GpsTransmit */ SDIO_STATUS GpsTransmit(PSDGPS_DEVICE pDevice) { UINT ii; struct uart_port *port = &pDevice->Port; struct circ_buf *xmit; xmit = ((port == NULL) || (port->info == NULL)) ? NULL : &port->info->xmit; /* if we aren't ready, just clear interrupt */ if (xmit == NULL) { GPS_STOP_TX(port); return SDIO_STATUS_SUCCESS; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { GPS_STOP_TX(port); return SDIO_STATUS_SUCCESS; } for(ii = 0; ii < GPS_FIFO_SIZE; ii++) { WriteRegister(pDevice, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) { break; } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { uart_write_wakeup(port); } if (uart_circ_empty(xmit)) { GPS_STOP_TX(port); } return SDIO_STATUS_SUCCESS; } /* * module init */ static int __init sdio_gps_init(void) { SDIO_STATUS status; SYSTEM_STATUS err; REL_PRINT(SDDBG_TRACE, ("SDIO GPS Function: init\n")); /* register with the serial driver core */ err = uart_register_driver(&gps_uart); if (err < 0) { DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: failed to register with uart driver, %d\n", err)); return err; } /* register with bus driver core */ if (!SDIO_SUCCESS((status = SDIO_RegisterFunction(&GpsContext.Function)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO GPS Function: failed to register with bus driver, %d\n", status)); uart_unregister_driver(&gps_uart); return SDIOErrorToOSError(status); } return err; } /* * module cleanup */ static void __exit sdio_gps_cleanup(void) { REL_PRINT(SDDBG_TRACE, ("SDIO GPS Function: : cleanup\n")); SDIO_UnregisterFunction(&GpsContext.Function); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: SDIO unregistered\n")); /* unregister with the serial driver core */ uart_unregister_driver(&gps_uart); DBG_PRINT(SDDBG_TRACE, ("SDIO GPS Function: UART unregistered\n")); } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DESCRIPTION); MODULE_AUTHOR(AUTHOR); module_init(sdio_gps_init); module_exit(sdio_gps_cleanup);