All Classes Namespaces Files Functions Variables Enumerations Enumerator
libs/connection/FindComPorts.cpp
Go to the documentation of this file.
00001 
00010 #include "FindComPorts.h"
00011 #include "DetectOS.h"
00012 #ifdef WINDOWS_OS
00013 #include <windows.h>
00014 #include <setupapi.h>
00015 #include <deque>
00016 #include <algorithm>
00017 
00018 #if defined(MSC)
00019 #pragma comment(lib, "setupapi.lib")
00020 #endif
00021 
00022 #else
00023 #include <deque>
00024 #include <algorithm>
00025 #include <cstring>
00026 #include <dirent.h>
00027 #include <sys/stat.h>
00028 #endif
00029 
00030 using namespace qrk;
00031 using namespace std;
00032 
00033 
00034 struct FindComPorts::pImpl
00035 {
00036     vector<string> base_names_;
00037     vector<string> driver_names_;
00038 
00039 
00040     pImpl(void)
00041     {
00042         // Windows はレジストリ情報だけから COM 一覧の探索を行う
00043         // よって、以降の設定は Linux, MacOS のみで使われる
00044 #if defined(LINUX_OS)
00045         base_names_.push_back("/dev/ttyUSB");
00046         base_names_.push_back("/dev/usb/ttyUSB");
00047 #elif defined(MAC_OS)
00048         base_names_.push_back("/dev/tty.usbmodem");
00049 #endif
00050     }
00051 
00052 
00053     void addBaseName(const char* base_name)
00054     {
00055         base_names_.insert(base_names_.begin(), base_name);
00056     }
00057 
00058 
00059     void addDriverName(const char* driver_name)
00060     {
00061         driver_names_.push_back(driver_name);
00062     }
00063 
00064 
00065     void orderByDriver(deque<string>& ports, deque<string>& drivers,
00066                        const char* driver_name,
00067                        bool all_ports)
00068     {
00069         if (ports.empty()) {
00070             return;
00071         }
00072 
00073         vector<string>::const_iterator it =
00074             std::find(driver_names_.begin(), driver_names_.end(), driver_name);
00075         if (it != driver_names_.end()) {
00076             ports.push_front(ports.back());
00077             ports.pop_back();
00078 
00079             drivers.push_front(drivers.back());
00080             drivers.pop_back();
00081 
00082         } else {
00083             if (! all_ports) {
00084                 ports.pop_back();
00085                 drivers.pop_back();
00086             }
00087         }
00088     }
00089 
00090 
00091     void addFoundPorts(vector<string>& found_ports,
00092                        const string& port)
00093     {
00094         for (vector<string>::iterator it = found_ports.begin();
00095              it != found_ports.end(); ++it) {
00096 
00097             // !!! データ構造を map にすべきかも
00098             // !!! 検索のアルゴリズムを用いるべき
00099 
00100             if (! port.compare(*it)) {
00101                 return;
00102             }
00103         }
00104         found_ports.push_back(port);
00105     }
00106 };
00107 
00108 
00109 FindComPorts::FindComPorts(void) : pimpl(new pImpl)
00110 {
00111 }
00112 
00113 
00114 FindComPorts::~FindComPorts(void)
00115 {
00116 }
00117 
00118 
00119 void FindComPorts::clearBaseNames(void)
00120 {
00121     pimpl->base_names_.clear();
00122 }
00123 
00124 
00125 void FindComPorts::addBaseName(const char* base_name)
00126 {
00127     pimpl->addBaseName(base_name);
00128 }
00129 
00130 
00131 vector<string> FindComPorts::baseNames(void)
00132 {
00133     return pimpl->base_names_;
00134 }
00135 
00136 
00137 void FindComPorts::addDriverName(const char* driver_name)
00138 {
00139     pimpl->addDriverName(driver_name);
00140 }
00141 
00142 
00143 #ifdef WINDOWS_OS
00144 // Windows の場合
00145 size_t FindComPorts::find(std::vector<std::string>& ports,
00146                           std::vector<std::string>& driver_names,
00147                           bool all_ports)
00148 {
00149     // デバイスマネージャの一覧から COM デバイスを探す
00150     deque<string> found_ports;
00151     deque<string> found_drivers;
00152 
00153     //4D36E978-E325-11CE-BFC1-08002BE10318
00154     GUID GUID_DEVINTERFACE_COM_DEVICE =
00155         {0x4D36E978L, 0xE325, 0x11CE,
00156          {0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 }
00157         };
00158 
00159     HDEVINFO hdi = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COM_DEVICE, 0, 0,
00160                                        DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
00161     if (hdi == INVALID_HANDLE_VALUE) {
00162         return 0;
00163     }
00164 
00165     SP_DEVINFO_DATA sDevInfo;
00166     sDevInfo.cbSize = sizeof(SP_DEVINFO_DATA);
00167     for (DWORD i = 0; SetupDiEnumDeviceInfo(hdi, i, &sDevInfo); ++i){
00168 
00169         enum { BufferSize = 256 };
00170         char buffer[BufferSize + 1];
00171         DWORD dwRegType;
00172         DWORD dwSize;
00173 
00174         // フレンドリーネームを取得して COM 番号を取り出す
00175         SetupDiGetDeviceRegistryPropertyA(hdi, &sDevInfo, SPDRP_FRIENDLYNAME,
00176                                           &dwRegType, (BYTE*)buffer, BufferSize,
00177                                           &dwSize);
00178         enum { ComNameLengthMax = 7 };
00179         size_t n = strlen(buffer);
00180         if (n < ComNameLengthMax) {
00181             // COM 名が短過ぎた場合、処理しない
00182             // 問題がある場合は、修正する
00183             continue;
00184         }
00185 
00186         // (COMx) の最後の括弧を探す
00187         for (int j = n - 1; j > 0; --j) {
00188             if (buffer[j] == ')') {
00189                 buffer[j] = '\0';
00190                 break;
00191             }
00192         }
00193         char* p = strstr(&buffer[n - ComNameLengthMax], "COM");
00194         if (! p) {
00195             continue;
00196         }
00197         found_ports.push_back(p);
00198 
00199         // デバイス名を取得し、IsUsbCom の条件に一致したら、先頭に配置する
00200         SetupDiGetDeviceRegistryPropertyA(hdi, &sDevInfo, SPDRP_DEVICEDESC,
00201                                           &dwRegType, (BYTE*)buffer, BufferSize,
00202                                           &dwSize);
00203         found_drivers.push_back(buffer);
00204         pimpl->orderByDriver(found_ports, found_drivers, buffer, all_ports);
00205     }
00206     SetupDiDestroyDeviceInfoList(hdi);
00207 
00208     ports.insert(ports.end(), found_ports.begin(), found_ports.end());
00209     driver_names.insert(driver_names.end(),
00210                         found_drivers.begin(), found_drivers.end());
00211 
00212     return ports.size();
00213 }
00214 
00215 
00216 #else
00217 
00218 namespace
00219 {
00220     void searchFiles(vector<string>& ports, const string& dir_name)
00221     {
00222         DIR* dp = opendir(dir_name.c_str());
00223         if (! dp) {
00224             return;
00225         }
00226 
00227         struct dirent* dir;
00228         while ((dir = readdir(dp))) {
00229             struct stat state;
00230             stat(dir->d_name, &state);
00231             if (S_ISDIR(state.st_mode)) {
00232                 string file = dir_name + dir->d_name;
00233                 ports.push_back(file);
00234             }
00235         }
00236     }
00237 }
00238 
00239 
00240 // Linux, Mac の場合
00241 size_t FindComPorts::find(std::vector<std::string>& ports,
00242                           std::vector<std::string>& driver_names,
00243                           bool all_ports)
00244 {
00245     static_cast<void>(all_ports);
00246     static_cast<void>(driver_names);
00247 
00248     vector<string> found_ports;
00249 
00250     // 登録ベース名毎に、ファイル名がないかの探索を行う
00251     for (vector<string>::iterator it = pimpl->base_names_.begin();
00252          it != pimpl->base_names_.end(); ++it) {
00253 
00254         // ディレクトリ名とファイル名との切り分け
00255         const char* path = it->c_str();
00256         const char* last_slash = strrchr(path, '/') + 1;
00257 
00258         string dir_name = it->substr(0, last_slash - path);
00259         vector<string> ports;
00260         searchFiles(ports, dir_name);
00261 
00262         size_t n = it->size();
00263         for (vector<string>::iterator fit = ports.begin();
00264              fit != ports.end(); ++fit) {
00265             if (! fit->compare(0, n, *it)) {
00266                 // マッチしたら、登録を行う
00267                 pimpl->addFoundPorts(found_ports, *fit);
00268             }
00269         }
00270     }
00271 
00272     for (vector<string>::iterator it = found_ports.begin();
00273          it != found_ports.end(); ++it) {
00274         ports.push_back(*it);
00275         driver_names.push_back("");
00276     }
00277 
00278     return found_ports.size();
00279 }
00280 #endif
00281 
00282 
00283 size_t FindComPorts::find(vector<string>& ports, bool all_ports)
00284 {
00285     vector<string> driver_names_dummy;
00286     return find(ports, driver_names_dummy, all_ports);
00287 }