#include #include #include // printk() #include // THIS_MODULE #include // struct kobject #include #include // error codes #include #include #include #include #include #include "defines.h" #include "kfile.h" #include "kspi.h" #include "ktiva.h" #include "sfsattrib.h" #include "kfirmware.h" #include "ksync.h" ///////////////////////////////////////////////////////////////////////////// MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("GfA"); #define _SLEEP_DELAY_JIFFIES 5 #define _IS_REVIVE_STATE(ret) ((ret) != -ECOMM) ///////////////////////////////////////////////////////////////////////////// // sys fs static struct kobject *g_pKoGfa = NULL, *g_pKoTiva = NULL, *g_pKoBacklight = NULL, *g_pKoADC = NULL, *g_pKoFirmware = NULL, *g_pKoMem = NULL, *g_pKoRtc = NULL; static bool g_bHasFwVersion = false, g_bHasDeviceCaps = false; static atomic_t g_flgSysFsRunning; ///////////////////////////////////////////////////////////////////////////// // worker threads static void _SysFsWorkProc(struct work_struct *work); static void _FwUploadWorkProc(struct work_struct *work); static void _BacklightWorkProc(struct work_struct *work); static struct workqueue_struct *g_pwq = NULL; static DECLARE_WORK(g_sysFsWorkObj, _SysFsWorkProc); static DECLARE_WORK(g_fwUploadWorkObj, _FwUploadWorkProc); static DECLARE_WORK(g_backLightWorkObj, _BacklightWorkProc); ///////////////////////////////////////////////////////////////////////////// // timer static atomic_t g_flgTimerRunning; static struct timer_list g_timer_list; ///////////////////////////////////////////////////////////////////////////// typedef enum _WorkerState { WS_SysFs, WS_FwUpload, WS_Backlight, WS_Idle }WorkerState; ///////////////////////////////////////////////////////////////////////////// static bool _GetSysFsRunning(void) { return !!atomic_read(&g_flgSysFsRunning); } ///////////////////////////////////////////////////////////////////////////// static void _SetSysFsRunning(bool bRunning) { atomic_set(&g_flgSysFsRunning, bRunning ? 1 : 0); } ///////////////////////////////////////////////////////////////////////////// static bool _GetTimerRunning(void) { return !!atomic_read(&g_flgTimerRunning); } ///////////////////////////////////////////////////////////////////////////// static void _SetTimerRunning(bool bRunning) { atomic_set(&g_flgTimerRunning, bRunning ? 1 : 0); } ///////////////////////////////////////////////////////////////////////////// static void _WorkScheduler(unsigned long ptr) { static unsigned int nPass = 0; static WorkerState ws = WS_SysFs; // KALERT("%s: %d, %p\n", __FUNCTION__, _GetTimerRunning() ? 1 : 0, g_pwq); if(_GetTimerRunning()) { if(KfwUploadInfoReady()) ws = WS_FwUpload; else if(SfAttBacklightChanged()) ws = WS_Backlight; else if(!_GetSysFsRunning()) ws = WS_Idle; switch(ws) { case WS_SysFs: if(++nPass == 3) { if(!queue_work(g_pwq, &g_sysFsWorkObj)) { KALERT("%s: queue_work failed\n", __FUNCTION__); } nPass = 0; } else { mod_timer(&g_timer_list, _TIMER_INTERVAL); } break; case WS_FwUpload: if(!queue_work(g_pwq, &g_fwUploadWorkObj)) { KALERT("%s: queue_work failed\n", __FUNCTION__); } ws = WS_SysFs; nPass = 2; break; case WS_Backlight: if(!queue_work(g_pwq, &g_backLightWorkObj)) { KALERT("%s: queue_work failed\n", __FUNCTION__); } ws = WS_SysFs; nPass = 2; break; case WS_Idle: mod_timer(&g_timer_list, _TIMER_INTERVAL); break; } } } ///////////////////////////////////////////////////////////////////////////// // work queue // sysfs_notify // https://stackoverflow.com/questions/16367623/using-the-linux-sysfs-notify-call // static void _BacklightWorkProc(struct work_struct *work) { struct file *pfSpiDev = NULL; int ret; if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0))) { do { unsigned int nPeriod = SfAttGetBacklightPeriod(); unsigned int nDutyCyclePerc = SfAttGetBacklightDutyCyclePercent(); ///////////////////////////////////////////////////////////////// if(!KSpiInit(pfSpiDev)) { break; } ///////////////////////////////////////////////////////////////// KALERT("%s: Set backlight period to %u, duty cycle to %u %%\n", __FUNCTION__, nPeriod, nDutyCyclePerc); if((ret = TivaCmdSetBacklight(pfSpiDev, nPeriod, nDutyCyclePerc)) != 0) { KALERT("%s: TivaCmdSetBacklight failed: %d!\n", __FUNCTION__, ret); break; } } while(0); kf_close(pfSpiDev); } else { KALERT("%s: kf_open_locked failed\n", __FUNCTION__); } if(_GetTimerRunning()) { ksync_lock(); mod_timer(&g_timer_list, _TIMER_INTERVAL); ksync_unlock(); } } ///////////////////////////////////////////////////////////////////////////// static void _FwUploadWorkProc(struct work_struct *work) { int nRet, i; bool bSuccess = false, bReset = true; unsigned long start = jiffies; struct file *pfSpiDev = NULL; TIVA_UPLOAD_INFO tui; if(!KfwQueryUploadInfo(&tui)) { KALERT("%s: Upload info not ready!!\n", __FUNCTION__); return; } if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0))) { do { unsigned char stat = 0xFF; size_t nCntBlocks, nCbToSend, nCbBlock, nCntFlashPages; const unsigned char *pbToSend; if(!KSpiInit(pfSpiDev)) { break; } ///////////////////////////////////////////////////////////////// if(g_sw >= KFW_MIN_HAS_MATERIAL_NR_VERSION) { } if((nRet = TivaCmdStartBootloader(pfSpiDev)) < 0) { KALERT("%s: TivaCmdStartBootloader failed!\n", __FUNCTION__); break; } ksync_lock(); g_hw = -1; g_sw = -1; g_bHasFwVersion = false; ksync_unlock(); sysfs_notify(g_pKoFirmware, NULL, "version"); KALERT("%s: Started bootloader!\n", __FUNCTION__); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); if((nRet = TivaCmdGetBootloaderStatus(pfSpiDev, &stat)) < 0) { KALERT("%s: TivaCmdGetBootloaderStatus failed!\n", __FUNCTION__); break; } KALERT("%s: Bootloader Status: 0x%02hhX\n", __FUNCTION__, stat); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); if((nRet = TivaCmdPing(pfSpiDev)) < 0) { KALERT("%s: TivaCmdPing failed!\n", __FUNCTION__); break; } KALERT("%s: Bootloader Ping success!\n", __FUNCTION__); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); if((nRet = TivaCmdGetBootloaderStatus(pfSpiDev, &stat)) < 0) { KALERT("%s: TivaCmdGetBootloaderStatus failed!\n", __FUNCTION__); break; } KALERT("%s: Bootloader Status: 0x%02hhX\n", __FUNCTION__, stat); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); ///////////////////////////////////////////////////////////////// nCbToSend = (tui.nCbData + (KFW_MIN_UPLOAD_BLOCKSIZE - 1) / KFW_MIN_UPLOAD_BLOCKSIZE * KFW_MIN_UPLOAD_BLOCKSIZE); pbToSend = (const unsigned char*)tui.pData; nCntBlocks = (nCbToSend + KFW_DEFAULT_UPLOAD_BLOCKSIZE - 1) / KFW_DEFAULT_UPLOAD_BLOCKSIZE; nCntFlashPages = (nCntBlocks * KFW_DEFAULT_UPLOAD_BLOCKSIZE + KFW_FLASH_PAGE_SIZE - 1) / KFW_FLASH_PAGE_SIZE; KALERT("%s: Start Download of %zu Bytes (%zu Blocks) at Address 0x%08X\n", __FUNCTION__, nCbToSend, nCntBlocks, KFW_DEFAULT_BASE_ADDRESS); if((nRet = TivaCmdStartDownload(pfSpiDev, KFW_DEFAULT_BASE_ADDRESS, nCbToSend)) != 0) { KALERT("%s: TivaCmdStartDownload failed: %d!\n", __FUNCTION__, nRet); break; } ksync_sleep_jiffies((long)(nCntFlashPages * KFW_DEFAULT_PAGE_ERASE_TIME)); if((nRet = TivaCmdGetBootloaderStatus(pfSpiDev, &stat)) < 0) { KALERT("%s: TivaCmdGetBootloaderStatus failed!\n", __FUNCTION__); break; } if(stat != COMMAND_RET_SUCCESS) { KALERT("%s: TivaCmdGetBootloaderStatus returned 0x%02hhX!\n", __FUNCTION__, stat); break; } KALERT("%s: Bootloader Status: 0x%02hhX\n", __FUNCTION__, stat); KALERT("%s: Sending data ...\n", __FUNCTION__); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); for(i = 0; i < nCntBlocks; ++i) { nCbBlock = _min(KFW_DEFAULT_UPLOAD_BLOCKSIZE, nCbToSend); if((nRet = TivaCmdSendDataBlock(pfSpiDev, pbToSend, nCbBlock)) != 0) { bReset = false; KALERT("%s: TivaCmdSendDataBlock failed!\n", __FUNCTION__); break; } pbToSend += nCbBlock; nCbToSend -= nCbBlock; if(i && !(i % 64)) { KALERT("%s: %zu Blocks sent.\n", __FUNCTION__, i); } // ksync_sleep_jiffies(1); if((nRet = TivaCmdGetBootloaderStatus(pfSpiDev, &stat)) < 0) { bReset = false; KALERT("%s: TivaCmdGetBootloaderStatus failed!\n", __FUNCTION__); break; } if(stat != COMMAND_RET_SUCCESS) { bReset = false; KALERT("%s: TivaCmdGetBootloaderStatus returned 0x%02hhX!\n", __FUNCTION__, stat); break; } // ksync_sleep_jiffies(1); } KALERT("%s: %zu Blocks sent.\n", __FUNCTION__, i); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); ///////////////////////////////////////////////////////////////// if(bReset) { if((nRet = TivaCmdReset(pfSpiDev)) < 0) { KALERT("%s: TivaCmdReset failed!\n", __FUNCTION__); break; } KALERT("%s: Started firmware!\n", __FUNCTION__); ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); if((nRet = TivaCmdGetStatus(pfSpiDev, &stat)) < 0) { KALERT("%s: TivaCmdGetStatus failed!\n", __FUNCTION__); break; } KALERT("%s: FW Status: 0x%02hhX\n", __FUNCTION__, stat); bSuccess = true; } // KALERT("%s: data: 0x%p, cb: %zu, mat: %s, bld: %s\n", __FUNCTION__, tui.pData, tui.nCbData, tui.pszMat, tui.pszBld); } while(0); kf_close(pfSpiDev); SfAttLockFirmware(false); } else { KALERT("%s: kf_open_locked failed\n", __FUNCTION__); } if(!bSuccess) { KALERT("%s: Firmware-Upload failed! Stopping all activities!\n", __FUNCTION__); } _SetSysFsRunning(bSuccess); if(_GetTimerRunning()) { ksync_lock(); mod_timer(&g_timer_list, _TIMER_INTERVAL); ksync_unlock(); } KALERT("%s: jiffies: %lu\n", __FUNCTION__, _JIFFY_DIFF(jiffies, start)); } ///////////////////////////////////////////////////////////////////////////// static void _SysFsWorkProc(struct work_struct *work) { TIVA_ADC tadc; unsigned char stat; int hw, sw, ret; unsigned long long nUpTime; struct file *pfSpiDev = NULL; if((pfSpiDev = kf_open_locked(_SPI_DEVICE, O_RDWR, 0))) { do { if(!KSpiInit(pfSpiDev)) { break; } ///////////////////////////////////////////////////////////////// #if !_SITARA_EGGELSBERG if( (ret = TivaCmdPing(pfSpiDev)) < 0 && _IS_REVIVE_STATE(ret)) { KALERT("%s: TivaCmdPing failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); } ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); ///////////////////////////////////////////////////////////////// // device capabilities if(!g_bHasDeviceCaps) { TIVA_DCAP dcap; if((ret = TivaCmdGetDeviceCaps(pfSpiDev, &dcap)) == 0) { ksync_lock(); g_dcaps = dcap; g_tiMM[0].size = KTIVA_FLASH_SIZE(dcap.fsize); // Flash g_tiMM[1].size = KTIVA_SRAM_SIZE(dcap.ssize); // SRAM g_tiMM[2].size = g_tiMM[1].size * 32; // Bit banded SRAM ksync_unlock(); g_bHasDeviceCaps = true; } else { if(_IS_REVIVE_STATE(ret)) { KALERT("%s: TivaCmdGetFirmwareVersion failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); } break; } ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); } ///////////////////////////////////////////////////////////////// // firmware if(!g_bHasFwVersion) { if((ret = TivaCmdGetFirmwareVersion(pfSpiDev, &hw, &sw)) == 0) { ksync_lock(); g_hw = hw; g_sw = sw; ksync_unlock(); sysfs_notify(g_pKoFirmware, NULL, "version"); // KALERT("%s: TivaCmdGetFirmwareVersion: HW %d, SW: %d\n", __FUNCTION__, g_hw, g_sw); g_bHasFwVersion = true; } else { if(_IS_REVIVE_STATE(ret)) { KALERT("%s: TivaCmdGetFirmwareVersion failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); } break; } ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); } ///////////////////////////////////////////////////////////////// // ADC if((ret = TivaCmdGetADC(pfSpiDev, &tadc)) == 0) { bool bModified = false; if(g_tadc.UVers != tadc.UVers) { ksync_lock(); g_tadc.UVers = tadc.UVers; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "UVers"); } if(g_tadc.UBatV3 != tadc.UBatV3) { ksync_lock(); g_tadc.UBatV3 = tadc.UBatV3; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "UBatV3"); } if(g_tadc.Temp != tadc.Temp) { ksync_lock(); g_tadc.Temp = tadc.Temp; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "TempBoard"); } if(g_tadc.UV5Vsys != tadc.UV5Vsys) { ksync_lock(); g_tadc.UV5Vsys = tadc.UV5Vsys; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "UV5Vsys"); } if(g_tadc.UV3V6Bat != tadc.UV3V6Bat) { ksync_lock(); g_tadc.UV3V6Bat = tadc.UV3V6Bat; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "UV3V6Bat"); } if(g_tadc.TempTIVA != tadc.TempTIVA) { ksync_lock(); g_tadc.TempTIVA = tadc.TempTIVA; ksync_unlock(); bModified = true; sysfs_notify(g_pKoADC, NULL, "TempTIVA"); } if(bModified) // if any of the ADC values have changed, signal the binary file { sysfs_notify(g_pKoADC, NULL, "AdcBin"); } } else if(_IS_REVIVE_STATE(ret)) { KALERT("%s: TivaCmdGetADC failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); } ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); ///////////////////////////////////////////////////////////////// if((ret = TivaCmdGetUptime(pfSpiDev, &nUpTime)) == 0) { if(g_nUpTime != nUpTime) { ksync_lock(); g_nUpTime = nUpTime; ksync_unlock(); sysfs_notify(g_pKoTiva, NULL, "uptime"); } } else if(_IS_REVIVE_STATE(ret)) { KALERT("%s: TivaCmdGetUptime failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); } ksync_sleep_jiffies(_SLEEP_DELAY_JIFFIES); ///////////////////////////////////////////////////////////////// #endif // _SITARA_EGGELSBERG if((ret = TivaCmdGetStatus(pfSpiDev, &stat)) == 0) { } else if(_IS_REVIVE_STATE(ret)) { #if !_SITARA_EGGELSBERG KALERT("%s: TivaCmdGetStatus failed: %d - call TivaRevive\n", __FUNCTION__, ret); TivaRevive(pfSpiDev); #endif // _SITARA_EGGELSBERG } ///////////////////////////////////////////////////////////////// // KALERT("Worker pass - jiffies: %ld - status: 0x%02hhX\n", _JIFFY_DIFF(jiffies, start), stat); } while(0); kf_close(pfSpiDev); } else { KALERT("%s: kf_open_locked failed\n", __FUNCTION__); } if(_GetTimerRunning()) { ksync_lock(); mod_timer(&g_timer_list, _TIMER_INTERVAL); ksync_unlock(); } } ///////////////////////////////////////////////////////////////////////////// static int drv_init(void) { ///////////////////////////////////////////////////////////////////////// do { if(!(g_pKoGfa = kobject_create_and_add("gfa", NULL))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoTiva = kobject_create_and_add("tiva", g_pKoGfa))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoBacklight = kobject_create_and_add("backlight", g_pKoTiva))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoADC = kobject_create_and_add("adc", g_pKoTiva))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoFirmware = kobject_create_and_add("firmware", g_pKoTiva))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoMem = kobject_create_and_add("mem", g_pKoTiva))) { KALERT("kobject_create_and_add failed\n"); break; } if(!(g_pKoRtc = kobject_create_and_add("rtc", g_pKoTiva))) { KALERT("kobject_create_and_add failed\n"); break; } ///////////////////////////////////////////////////////////////////// // generic if(sysfs_create_file(g_pKoTiva, &g_tivaDevIdAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoTiva, &g_tivaUptimeAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } ///////////////////////////////////////////////////////////////////// // firmware if(sysfs_create_file(g_pKoFirmware, &g_tivaVersionAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(SfAttInit()) { if(sysfs_create_bin_file(g_pKoFirmware, &g_tivaFwImageAtt)) { KALERT("sysfs_create_file failed\n"); break; } } ///////////////////////////////////////////////////////////////////// // backlight if(sysfs_create_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoBacklight, &g_tivaPeriodAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } ///////////////////////////////////////////////////////////////////// // ADC if(sysfs_create_file(g_pKoADC, &g_tivaUVersAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoADC, &g_tivaUBatV3Att.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoADC, &g_tivaTempAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoADC, &g_tivaUV5VsysAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoADC, &g_tivaUV3V6BatAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoADC, &g_tivaTempTIVAAtt.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_bin_file(g_pKoADC, &g_tivaAdcBinAtt)) { KALERT("sysfs_create_file failed\n"); break; } ///////////////////////////////////////////////////////////////////// // Memory if(sysfs_create_bin_file(g_pKoMem, &g_tivaMemory)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoMem, &g_tivaMemMap.attr)) { KALERT("sysfs_create_file failed\n"); break; } ///////////////////////////////////////////////////////////////////// // RTC if(sysfs_create_file(g_pKoRtc, &g_tivaRtcIso8601.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoRtc, &g_tivaRtcCtrl.attr)) { KALERT("sysfs_create_file failed\n"); break; } if(sysfs_create_file(g_pKoRtc, &g_tivaRtcType.attr)) { KALERT("sysfs_create_file failed\n"); break; } ///////////////////////////////////////////////////////////////////// if(!(g_pwq = create_workqueue("GfaWrk"))) { KALERT("create_workqueue failed!\n"); break; } ///////////////////////////////////////////////////////////////////// init_timer(&g_timer_list); g_timer_list.function = _WorkScheduler; g_timer_list.data = 0; g_timer_list.expires = _TIMER_INTERVAL; add_timer(&g_timer_list); _SetTimerRunning(true); ///////////////////////////////////////////////////////////////////// _SetSysFsRunning(true); if(!queue_work(g_pwq, &g_sysFsWorkObj)) { KALERT("%s: queue_work failed\n", __FUNCTION__); break; } ///////////////////////////////////////////////////////////////////// KALERT("%s, Ctx: \"%s\"-(%d), HZ: %d\n", __FUNCTION__, current->comm, current->pid, HZ); return 0; } while(0); ///////////////////////////////////////////////////////////////////////// _SetSysFsRunning(false); if(_GetTimerRunning()) { _SetTimerRunning(false); del_timer_sync(&g_timer_list); } if(g_pwq) { destroy_workqueue(g_pwq); g_pwq = NULL; } SfAttExit(); if(g_pKoRtc) { sysfs_remove_file(g_pKoRtc, &g_tivaRtcType.attr); sysfs_remove_file(g_pKoRtc, &g_tivaRtcCtrl.attr); sysfs_remove_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64); sysfs_remove_file(g_pKoRtc, &g_tivaRtcIso8601.attr); kobject_put(g_pKoRtc); g_pKoRtc = NULL; } if(g_pKoMem) { sysfs_remove_file(g_pKoMem, &g_tivaMemMap.attr); sysfs_remove_bin_file(g_pKoMem, &g_tivaMemory); kobject_put(g_pKoMem); g_pKoMem = NULL; } if(g_pKoFirmware) { sysfs_remove_bin_file(g_pKoFirmware, &g_tivaFwImageAtt); sysfs_remove_file(g_pKoFirmware, &g_tivaVersionAtt.attr); kobject_put(g_pKoFirmware); g_pKoFirmware = NULL; } if(g_pKoADC) { sysfs_remove_file(g_pKoADC, &g_tivaUVersAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUBatV3Att.attr); sysfs_remove_file(g_pKoADC, &g_tivaTempAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUV5VsysAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUV3V6BatAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaTempTIVAAtt.attr); sysfs_remove_bin_file(g_pKoADC, &g_tivaAdcBinAtt); kobject_put(g_pKoADC); g_pKoADC = NULL; } if(g_pKoBacklight) { sysfs_remove_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr); sysfs_remove_file(g_pKoBacklight, &g_tivaPeriodAtt.attr); kobject_put(g_pKoBacklight); g_pKoBacklight = NULL; } if(g_pKoTiva) { sysfs_remove_file(g_pKoTiva, &g_tivaUptimeAtt.attr); sysfs_remove_file(g_pKoTiva, &g_tivaDevIdAtt.attr); kobject_put(g_pKoTiva); g_pKoTiva = NULL; } if(g_pKoGfa) { kobject_put(g_pKoGfa); g_pKoGfa = NULL; } return -ENOMEM; } ///////////////////////////////////////////////////////////////////////////// static void drv_exit(void) { _SetSysFsRunning(false); if(_GetTimerRunning()) { _SetTimerRunning(false); del_timer_sync(&g_timer_list); } if(g_pwq) { destroy_workqueue(g_pwq); g_pwq = NULL; } SfAttExit(); if(g_pKoRtc) { sysfs_remove_file(g_pKoRtc, &g_tivaRtcType.attr); sysfs_remove_file(g_pKoRtc, &g_tivaRtcCtrl.attr); sysfs_remove_bin_file(g_pKoRtc, &g_tivaRtcUnTsBin64); sysfs_remove_file(g_pKoRtc, &g_tivaRtcIso8601.attr); kobject_put(g_pKoRtc); g_pKoRtc = NULL; } if(g_pKoMem) { sysfs_remove_file(g_pKoMem, &g_tivaMemMap.attr); sysfs_remove_bin_file(g_pKoMem, &g_tivaMemory); kobject_put(g_pKoMem); g_pKoMem = NULL; } if(g_pKoFirmware) { sysfs_remove_bin_file(g_pKoFirmware, &g_tivaFwImageAtt); sysfs_remove_file(g_pKoFirmware, &g_tivaVersionAtt.attr); kobject_put(g_pKoFirmware); g_pKoFirmware = NULL; } if(g_pKoADC) { sysfs_remove_file(g_pKoADC, &g_tivaUVersAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUBatV3Att.attr); sysfs_remove_file(g_pKoADC, &g_tivaTempAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUV5VsysAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaUV3V6BatAtt.attr); sysfs_remove_file(g_pKoADC, &g_tivaTempTIVAAtt.attr); sysfs_remove_bin_file(g_pKoADC, &g_tivaAdcBinAtt); kobject_put(g_pKoADC); g_pKoADC = NULL; } if(g_pKoBacklight) { sysfs_remove_file(g_pKoBacklight, &g_tivaDutyCycleAtt.attr); sysfs_remove_file(g_pKoBacklight, &g_tivaPeriodAtt.attr); kobject_put(g_pKoBacklight); g_pKoBacklight = NULL; } if(g_pKoTiva) { sysfs_remove_file(g_pKoTiva, &g_tivaUptimeAtt.attr); sysfs_remove_file(g_pKoTiva, &g_tivaDevIdAtt.attr); kobject_put(g_pKoTiva); g_pKoTiva = NULL; } if(g_pKoGfa) kobject_put(g_pKoGfa); KALERT("%s, Ctx: \"%s\"-(%d)\n", __FUNCTION__, current->comm, current->pid); } ///////////////////////////////////////////////////////////////////////////// module_init(drv_init); module_exit(drv_exit);