aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/um/service/src/device.cpp
blob: 870930f48dfd4958ae7e30fb7fc49e61bae5c538 (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
// Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "Windows 7 drivers" project.
// For details, see https://github.com/egor-tensin/windows7-drivers.
// Distributed under the MIT License.

#include "service/all.hpp"

#include <Windows.h>

#include <cstddef>

#include <limits>
#include <stdexcept>
#include <string>

namespace service
{
    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 windows_error::make(ec, __FILE__, __LINE__, __FUNCTION__);
            }

            return Handle(raw);
        }
    }

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

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

        if (in_buf_size > std::numeric_limits<DWORD>::max())
            throw std::range_error("input buffer size is too large");

        std::size_t nbwritten = DeviceIoControl(
            handle,
            code,
            const_cast<void*>(in_buf),
            static_cast<DWORD>(in_buf_size),
            NULL,
            0,
            &nbreq,
            NULL);

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

            switch (ec)
            {
                case ERROR_MORE_DATA:
                    return nbreq;

                default:
                    throw windows_error::make(ec, __FILE__, __LINE__, __FUNCTION__);
            }
        }

        return nbwritten;
    }

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

        if (in_buf_size > std::numeric_limits<DWORD>::max())
            throw std::range_error("input buffer size is too large");
        if (out_buf_size > std::numeric_limits<DWORD>::max())
            throw std::range_error("output buffer size is too large");

        std::size_t nbwritten = DeviceIoControl(
            handle,
            code,
            const_cast<void*>(in_buf),
            static_cast<DWORD>(in_buf_size),
            out_buf,
            static_cast<DWORD>(out_buf_size),
            &nbreq,
            NULL);

        if (0 == nbwritten)
        {
            const auto ec = GetLastError();
            throw windows_error::make(ec, __FILE__, __LINE__, __FUNCTION__);
        }

        return nbwritten;
    }
}