aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/utils/libservice/src/device.cpp
blob: 3cfb760ba635e28d5640b8fe3642c01dfdbddee2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
 * \file
 * \author Egor Tensin <Egor.Tensin@gmail.com>
 * \date 2015
 * \copyright This file is licensed under the terms of the MIT License.
 *            See LICENSE.txt for details.
 */

#include "libservice/common.hpp"
#include "libservice/device.hpp"
#include "libservice/handle.hpp"
#include "libservice/windows_error.hpp"

#include <Windows.h>

#include <string>
#include <system_error>
#include <utility>

namespace libservice
{
    namespace
    {
        Handle open_device(const std::string& path)
        {
            const auto raw = CreateFileA(path.c_str(),
                                         GENERIC_READ | GENERIC_WRITE,
                                         0,
                                         NULL,
                                         OPEN_EXISTING,
                                         FILE_ATTRIBUTE_NORMAL,
                                         NULL);

            if (INVALID_HANDLE_VALUE == raw)
            {
                const auto ec = GetLastError();
                throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
            }

            return Handle(raw);
        }
    }

    Device Device::open(const std::string& path)
    {
        return Device(open_device(path));
    }

    DWORD Device::get_required_output_size(
        DWORD code,
        const void* in_buf,
        DWORD in_buf_size) const
    {
        DWORD nbreq;

        DWORD ret = DeviceIoControl(
            static_cast<HANDLE>(m_handle),
            code,
            const_cast<void*>(in_buf),
            in_buf_size,
            NULL,
            0,
            &nbreq,
            NULL);

        if (0 == ret)
        {
            const auto ec = GetLastError();

            switch (ec)
            {
                case ERROR_MORE_DATA:
                    return nbreq;

                default:
                    throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
            }
        }

        return ret;
    }

    DWORD Device::send_control_code(
        DWORD code,
        const void* in_buf,
        DWORD in_buf_size,
        void* out_buf,
        DWORD out_buf_size) const
    {
        DWORD nbreq;

        DWORD ret = DeviceIoControl(
            static_cast<HANDLE>(m_handle),
            code,
            const_cast<void*>(in_buf),
            in_buf_size,
            out_buf,
            out_buf_size,
            &nbreq,
            NULL);

        if (0 == ret)
        {
            const auto ec = GetLastError();
            throw std::system_error(ec, WinErrorCategory::get(), LIBSERVICE_ERROR_PREFIX);
        }

        return ret;
    }

    void swap(Device& a, Device& b) LIBSERVICE_NOEXCEPT
    {
        a.swap(b);
    }
}

namespace std
{
    template <>
    void swap<libservice::Device>(
        libservice::Device& a,
        libservice::Device& b)
    {
        a.swap(b);
    }
}