summaryrefslogtreecommitdiffstats
path: root/demo3/common/agl-service-windowmanager/src/wm_connection.cpp
blob: 10ecc3bbfcc70fab1161158a692ab7f999fad5ad (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/*
 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "wm_connection.hpp"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "json_helper.hpp"
#include "util.hpp"

extern "C"
{
#include <afb/afb-binding.h>
#include <systemd/sd-event.h>
}


/**
 * namespace wm
 */
namespace wm
{


namespace
{

static const char kPathConnectionConfigFile[] = "/etc/connection.json";
static const char kDefaultIpAddr[] = "192.168.10.10";
static const int  kDefaultPort     = 4000;

static int onIoEventReceive(sd_event_source *src, int fd, uint32_t revents, void * data)
{
    WMConnection *p_wmcon = (WMConnection*)data;

    json_object *j_out;
    int ret = p_wmcon->receive(&j_out);
    if (0 > ret)
    {
        return 0;
    }

    const char* rq = jh::getStringFromJson(j_out, "req");
    const char* id = jh::getStringFromJson(j_out, "appid");
    const char* dn = jh::getStringFromJson(j_out, "drawing_name");
    const char* da = jh::getStringFromJson(j_out, "drawing_area");

    HMI_DEBUG("req:%s appid:%s, drawing_name:%s, drawing_area:%s", rq, id, dn, da);

    // Callback
    p_wmcon->callOnReceivedHandler(j_out);

    return 0;
}

static int onIoEventAccept(sd_event_source *src, int fd, uint32_t revents, void * data)
{
    struct sockaddr_in addr;

    WMConnection *p_wmcon = (WMConnection*)data;

    // Accept connection
    socklen_t len = sizeof(addr);
    int my_socket = p_wmcon->getMySocket();
    int connected_socket = accept(my_socket, (struct sockaddr *)&addr, &len);
    if (0 > connected_socket)
    {
        HMI_ERROR("Failed to accept connection (%s)", strerror(errno));
        return -1;
    }

    // Store connected socket
    p_wmcon->setConnectedSocket(connected_socket);

    // Register callback to receive
    int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
                          connected_socket, EPOLLIN,
                          onIoEventReceive, p_wmcon);
    if (0 > ret)
    {
        HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret));
        return -1;
    }

    return 0;
}

} // namespace

WMConnection::WMConnection()
{
    // Load connection config file
    this->loadConnectionConfigFile();

    // TODO: ECU name should be decide by config file
    this->ecu_name = this->mode;
}

int WMConnection::initialize()
{
    int ret;

    // Initialize for Master/Slave
    if (this->isMasterMode())
    {
        ret = this->initializeMaster();
    }
    else
    {
        ret = this->initializeSlave();
    }

    return ret;
}

void WMConnection::registerCallback(ReceivedHandler on_received)
{
    this->onReceived = on_received;
}

int WMConnection::sendRequest(char const *req, char const *appid,
                              char const *drawing_name, char const *drawing_area)
{
    int ret;
    json_object *j_obj = json_object_new_object();
    json_object_object_add(j_obj, "req",          json_object_new_string(req));
    json_object_object_add(j_obj, "appid",        json_object_new_string(appid));
    json_object_object_add(j_obj, "drawing_name", json_object_new_string(drawing_name));
    json_object_object_add(j_obj, "drawing_area", json_object_new_string(drawing_area));

    ret = this->send(j_obj);

    json_object_put(j_obj);

    return ret;
}

int WMConnection::send(struct json_object* j_in)
{
    // Convert json_object to string to send
    const char *buf = json_object_to_json_string(j_in);
    if (nullptr == buf)
    {
        HMI_ERROR("Failed to convert json_object to string");
        return -1;
    }

    int len = strlen(buf);

    HMI_DEBUG("Send data(len:%d): %s", len, buf);

    int n = write(this->connected_socket, buf, len);
    if(0 > n)
    {
        HMI_ERROR("Failed to send data (%s)", strerror(errno));
        return -1;
    }

    return 0;
}

bool WMConnection::isMasterMode()
{
    if ("master" == this->mode)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool WMConnection::isMasterArea(const char* area)
{
    if (nullptr == area)
    {
        return false;
    }

    std::string str_area = std::string(area);
    if ("" == str_area)
    {
        return false;
    }

    std::vector<std::string> elements;
    elements = parseString(str_area, '.');

    if ("master" == elements[0])
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool WMConnection::isConnecting()
{
    return (0 > this->connected_socket) ? false : true;
}

std::string WMConnection::parseMasterArea(const char* area)
{
    std::string ret_area = "";
    std::vector<std::string> elements;
    elements = parseString(std::string(area), '.');

    if ("master" != elements[0])
    {
        return std::string(area);
    }

    for (auto itr = (elements.begin() + 1); itr != elements.end(); ++itr)
    {
        ret_area += *itr;

        if ((elements.end() - 1) != itr)
        {
            ret_area += ".";
        }
    }
    return ret_area;
}

bool WMConnection::isSyncDrawingForRemote(const char* appid)
{
    if (std::string(appid) == this->syndDrawingAppId)
    {
        return true;
    }
    else
    {
        return false;
    }
}

void WMConnection::startSyncDrawForRemote(const char* appid)
{
    this->syndDrawingAppId = std::string(appid);
}

void WMConnection::finishSyncDrawForRemote(const char* appid)
{
    if (std::string(appid) == this->syndDrawingAppId)
    {
        this->syndDrawingAppId = "";
    }
}

int WMConnection::getMySocket()
{
    return this->my_socket;
}

int WMConnection::getConnectedSocket()
{
    return this->connected_socket;
}

void WMConnection::setConnectedSocket(int connected_socket)
{
    this->connected_socket = connected_socket;
}

std::string WMConnection::getEcuName()
{
    return this->ecu_name;
}

void WMConnection::callOnReceivedHandler(json_object *j_out)
{
    this->onReceived(j_out);
}

int WMConnection::initializeMaster()
{
    int ret = 0;
    struct sockaddr_in addr;

    // Create socket
    this->my_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > this->my_socket)
    {
        HMI_ERROR("Failed to create socket (%s)", strerror(errno));
        return -1;
    }

    // Bind socket
    addr.sin_family = AF_INET;
    addr.sin_port = htons(this->port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(this->my_socket, (struct sockaddr *)&addr, sizeof(addr));
    if (0 > ret)
    {
        HMI_ERROR("Failed to bind socket (%s)", strerror(errno));
        return -1;
    }

    // Listen connection
    ret = listen(this->my_socket, 1);
    if (0 > ret)
    {
        HMI_ERROR("Failed to listen connection (%s)", strerror(errno));
        return -1;
    }

    // Register callback to accept connection
    ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
                          this->my_socket, EPOLLIN,
                          onIoEventAccept, this);
    if (0 > ret)
    {
        HMI_ERROR("Failed to add I/O event accept(%s)", strerror(-ret));
        return -1;
    }

    return ret;
}

int WMConnection::initializeSlave()
{
    // Create socket
    this->my_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > this->my_socket)
    {
        HMI_ERROR("Failed to create socket (%s)", strerror(errno));
        return -1;
    }

    return 0;
}

int WMConnection::connectToMaster()
{
    int ret = 0;
    struct sockaddr_in addr;

    // Connect to master
    addr.sin_family = AF_INET;
    addr.sin_port = htons(this->port);
    addr.sin_addr.s_addr = inet_addr(this->ip.c_str());

    ret = connect(this->my_socket, (struct sockaddr *)&addr, sizeof(addr));
    if (0 > ret)
    {
        HMI_ERROR("Failed to connect to master (%s)", strerror(errno));
        return ret;
    }

    HMI_DEBUG("Connected to master");

    // Store connected socket
    this->connected_socket = this->my_socket;

    // Register callback to receive
    ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
                          this->connected_socket, EPOLLIN,
                          onIoEventReceive, this);
    if (0 > ret)
    {
        HMI_ERROR("Failed to add I/O event receive(%s)", strerror(-ret));
        return -1;
    }

    return ret;
}

int WMConnection::receive(struct json_object** j_out)
{
    char buf[1024];
    int n;

    n = read(this->connected_socket, buf, sizeof(buf));
    if(0 > n)
    {
        HMI_ERROR("Failed to receive data (%s)", strerror(errno));
        return -1;
    }

    HMI_DEBUG("Received data length: %d", n);
    HMI_DEBUG("Received data: %s", buf);

    // Parse received data
    struct json_tokener *tokener = json_tokener_new();
    *j_out = json_tokener_parse_ex(tokener, buf, n);
    if (nullptr == *j_out)
    {
        HMI_DEBUG("Failed to parse received data");
        return -1;
    }

    return 0;
}

int WMConnection::loadConnectionConfigFile()
{
    // Get afm application installed dir
    char const *afm_app_install_dir = getenv("AFM_APP_INSTALL_DIR");
    if (!afm_app_install_dir)
    {
        HMI_ERROR("AFM_APP_INSTALL_DIR is not defined");
    }
    std::string path = std::string(afm_app_install_dir) + std::string(kPathConnectionConfigFile);

    // Load connection config file
    json_object* json_obj;
    int ret = jh::inputJsonFilie(path.c_str(), &json_obj);
    if (0 > ret)
    {
        HMI_ERROR("Could not open %s, so use default mode \"slave\"", kPathConnectionConfigFile);
        this->mode = "slave";
        this->ip   = kDefaultIpAddr;
        this->port = kDefaultPort;
        return 0;
    }
    HMI_DEBUG("json_obj dump:%s", json_object_get_string(json_obj));

    const char* mode = jh::getStringFromJson(json_obj, "mode");
    this->mode = (nullptr != mode) ? mode : "slave";

    const char* ip = jh::getStringFromJson(json_obj, "master_ip");
    this->ip = (nullptr != ip) ? ip : kDefaultIpAddr;

    int port = jh::getIntFromJson(json_obj, "master_port");
    this->port = (0 != port) ? port : kDefaultPort;

    // Check
    HMI_DEBUG("mode:%s master_ip:%s master_port:%d", mode, ip, port);

    // Release json_object
    json_object_put(json_obj);

    return 0;
}


} // namespace wm
hdm_ch = dev->hch + ch_idx; struct dim_ch_state_t st; struct list_head *head; struct mbo *mbo; int done_buffers; unsigned long flags; u8 *data; BUG_ON(!hdm_ch); BUG_ON(!hdm_ch->is_initialized); spin_lock_irqsave(&dim_lock, flags); done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers; if (!done_buffers) { spin_unlock_irqrestore(&dim_lock, flags); return; } if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) { spin_unlock_irqrestore(&dim_lock, flags); return; } spin_unlock_irqrestore(&dim_lock, flags); head = &hdm_ch->started_list; while (done_buffers) { spin_lock_irqsave(&dim_lock, flags); if (list_empty(head)) { spin_unlock_irqrestore(&dim_lock, flags); pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n"); break; } mbo = list_entry(head->next, struct mbo, list); list_del(head->next); spin_unlock_irqrestore(&dim_lock, flags); data = mbo->virt_address; if (hdm_ch->data_type == MOST_CH_ASYNC && hdm_ch->direction == MOST_CH_RX && PACKET_IS_NET_INFO(data)) { retrieve_netinfo(dev, mbo); spin_lock_irqsave(&dim_lock, flags); list_add_tail(&mbo->list, &hdm_ch->pending_list); spin_unlock_irqrestore(&dim_lock, flags); } else { if (hdm_ch->data_type == MOST_CH_CONTROL || hdm_ch->data_type == MOST_CH_ASYNC) { u32 const data_size = (u32)data[0] * 256 + data[1] + 2; mbo->processed_length = min_t(u32, data_size, mbo->buffer_length); } else { mbo->processed_length = mbo->buffer_length; } mbo->status = MBO_SUCCESS; mbo->complete(mbo); } done_buffers--; } } static struct dim_channel **get_active_channels(struct dim2_hdm *dev, struct dim_channel **buffer) { int idx = 0; int ch_idx; for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { if (dev->hch[ch_idx].is_initialized) buffer[idx++] = &dev->hch[ch_idx].ch; } buffer[idx++] = NULL; return buffer; } /** * dim2_tasklet_fn - tasklet function * @data: private data * * Service each initialized channel, if needed */ static void dim2_tasklet_fn(unsigned long data) { struct dim2_hdm *dev = (struct dim2_hdm *)data; unsigned long flags; int ch_idx; for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { if (!dev->hch[ch_idx].is_initialized) continue; spin_lock_irqsave(&dim_lock, flags); dim_service_channel(&dev->hch[ch_idx].ch); spin_unlock_irqrestore(&dim_lock, flags); service_done_flag(dev, ch_idx); while (!try_start_dim_transfer(dev->hch + ch_idx)) continue; } } /** * dim2_ahb_isr - interrupt service routine * @irq: irq number * @_dev: private data * * Acknowledge the interrupt and schedule a tasklet to service channels. * Return IRQ_HANDLED. */ static irqreturn_t dim2_ahb_isr(int irq, void *_dev) { struct dim2_hdm *dev = _dev; struct dim_channel *buffer[DMA_CHANNELS + 1]; unsigned long flags; spin_lock_irqsave(&dim_lock, flags); dim_service_irq(get_active_channels(dev, buffer)); spin_unlock_irqrestore(&dim_lock, flags); #if !defined(ENABLE_HDM_TEST) dim2_tasklet.data = (unsigned long)dev; tasklet_schedule(&dim2_tasklet); #else dim2_tasklet_fn((unsigned long)dev); #endif return IRQ_HANDLED; } #if defined(ENABLE_HDM_TEST) /* * Utility function used by HAL-simu for calling DIM interrupt handler. * It is used only for TEST PURPOSE. */ void raise_dim_interrupt(void) { (void)dim2_ahb_isr(0, test_dev); } #endif /** * complete_all_mbos - complete MBO's in a list * @head: list head * * Delete all the entries in list and return back MBO's to mostcore using * completion call back. */ static void complete_all_mbos(struct list_head *head) { unsigned long flags; struct mbo *mbo; for (;;) { spin_lock_irqsave(&dim_lock, flags); if (list_empty(head)) { spin_unlock_irqrestore(&dim_lock, flags); break; } mbo = list_entry(head->next, struct mbo, list); list_del(head->next); spin_unlock_irqrestore(&dim_lock, flags); mbo->processed_length = 0; mbo->status = MBO_E_CLOSE; mbo->complete(mbo); } } /** * configure_channel - initialize a channel * @iface: interface the channel belongs to * @channel: channel to be configured * @channel_config: structure that holds the configuration information * * Receives configuration information from mostcore and initialize * the corresponding channel. Return 0 on success, negative on failure. */ static int configure_channel(struct most_interface *most_iface, int ch_idx, struct most_channel_config *ccfg) { struct dim2_hdm *dev = iface_to_hdm(most_iface); bool const is_tx = ccfg->direction == MOST_CH_TX; u16 const sub_size = ccfg->subbuffer_size; u16 const buf_size = ccfg->buffer_size; u16 new_size; unsigned long flags; u8 hal_ret; int const ch_addr = ch_idx * 2 + 2; struct hdm_channel *const hdm_ch = dev->hch + ch_idx; BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); if (hdm_ch->is_initialized) return -EPERM; switch (ccfg->data_type) { case MOST_CH_CONTROL: new_size = dim_norm_ctrl_async_buffer_size(buf_size); if (new_size == 0) { pr_err("%s: too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) pr_warn("%s: fixed buffer size (%d -> %d)\n", hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, buf_size); break; case MOST_CH_ASYNC: new_size = dim_norm_ctrl_async_buffer_size(buf_size); if (new_size == 0) { pr_err("%s: too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) pr_warn("%s: fixed buffer size (%d -> %d)\n", hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, buf_size); break; case MOST_CH_ISOC_AVP: new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); if (new_size == 0) { pr_err("%s: invalid sub-buffer size or too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) pr_warn("%s: fixed buffer size (%d -> %d)\n", hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); break; case MOST_CH_SYNC: new_size = dim_norm_sync_buffer_size(buf_size, sub_size); if (new_size == 0) { pr_err("%s: invalid sub-buffer size or too small buffer size\n", hdm_ch->name); return -EINVAL; } ccfg->buffer_size = new_size; if (new_size != buf_size) pr_warn("%s: fixed buffer size (%d -> %d)\n", hdm_ch->name, buf_size, new_size); spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); break; default: pr_err("%s: configure failed, bad channel type: %d\n", hdm_ch->name, ccfg->data_type); return -EINVAL; } if (hal_ret != DIM_NO_ERROR) { spin_unlock_irqrestore(&dim_lock, flags); pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); return -ENODEV; } hdm_ch->data_type = ccfg->data_type; hdm_ch->direction = ccfg->direction; hdm_ch->is_initialized = true; if (hdm_ch->data_type == MOST_CH_ASYNC && hdm_ch->direction == MOST_CH_TX && dev->atx_idx < 0) dev->atx_idx = ch_idx; spin_unlock_irqrestore(&dim_lock, flags); return 0; } /** * enqueue - enqueue a buffer for data transfer * @iface: intended interface * @channel: ID of the channel the buffer is intended for * @mbo: pointer to the buffer object * * Push the buffer into pending_list and try to transfer one buffer from * pending_list. Return 0 on success, negative on failure. */ static int enqueue(struct most_interface *most_iface, int ch_idx, struct mbo *mbo) { struct dim2_hdm *dev = iface_to_hdm(most_iface); struct hdm_channel *hdm_ch = dev->hch + ch_idx; unsigned long flags; BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); if (!hdm_ch->is_initialized) return -EPERM; if (mbo->bus_address == 0) return -EFAULT; spin_lock_irqsave(&dim_lock, flags); list_add_tail(&mbo->list, &hdm_ch->pending_list); spin_unlock_irqrestore(&dim_lock, flags); (void)try_start_dim_transfer(hdm_ch); return 0; } /** * request_netinfo - triggers retrieving of network info * @iface: pointer to the interface * @channel_id: corresponding channel ID * * Send a command to INIC which triggers retrieving of network info by means of * "Message exchange over MDP/MEP". Return 0 on success, negative on failure. */ static void request_netinfo(struct most_interface *most_iface, int ch_idx) { struct dim2_hdm *dev = iface_to_hdm(most_iface); struct mbo *mbo; u8 *data; if (dev->atx_idx < 0) { pr_err("Async Tx Not initialized\n"); return; } mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL); if (!mbo) return; mbo->buffer_length = 5; data = mbo->virt_address; data[0] = 0x00; /* PML High byte */ data[1] = 0x03; /* PML Low byte */ data[2] = 0x02; /* PMHL */ data[3] = 0x08; /* FPH */ data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */ most_submit_mbo(mbo); } /** * poison_channel - poison buffers of a channel * @iface: pointer to the interface the channel to be poisoned belongs to * @channel_id: corresponding channel ID * * Destroy a channel and complete all the buffers in both started_list & * pending_list. Return 0 on success, negative on failure. */ static int poison_channel(struct most_interface *most_iface, int ch_idx) { struct dim2_hdm *dev = iface_to_hdm(most_iface); struct hdm_channel *hdm_ch = dev->hch + ch_idx; unsigned long flags; u8 hal_ret; int ret = 0; BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); if (!hdm_ch->is_initialized) return -EPERM; spin_lock_irqsave(&dim_lock, flags); hal_ret = dim_destroy_channel(&hdm_ch->ch); hdm_ch->is_initialized = false; if (ch_idx == dev->atx_idx) dev->atx_idx = -1; spin_unlock_irqrestore(&dim_lock, flags); if (hal_ret != DIM_NO_ERROR) { pr_err("HAL Failed to close channel %s\n", hdm_ch->name); ret = -EFAULT; } complete_all_mbos(&hdm_ch->started_list); complete_all_mbos(&hdm_ch->pending_list); return ret; } /* * dim2_probe - dim2 probe handler * @pdev: platform device structure * * Register the dim2 interface with mostcore and initialize it. * Return 0 on success, negative on failure. */ static int dim2_probe(struct platform_device *pdev) { struct dim2_hdm *dev; struct resource *res; int ret, i; struct kobject *kobj; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->atx_idx = -1; platform_set_drvdata(pdev, dev); #if defined(ENABLE_HDM_TEST) test_dev = dev; #else res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { pr_err("no memory region defined\n"); ret = -ENOENT; goto err_free_dev; } if (!request_mem_region(res->start, resource_size(res), pdev->name)) { pr_err("failed to request mem region\n"); ret = -EBUSY; goto err_free_dev; } dev->io_base = ioremap(res->start, resource_size(res)); if (!dev->io_base) { pr_err("failed to ioremap\n"); ret = -ENOMEM; goto err_release_mem; } ret = platform_get_irq(pdev, 0); if (ret < 0) { pr_err("failed to get irq\n"); goto err_unmap_io; } dev->irq_ahb0 = ret; ret = request_irq(dev->irq_ahb0, dim2_ahb_isr, 0, "mlb_ahb0", dev); if (ret) { pr_err("failed to request IRQ: %d, err: %d\n", dev->irq_ahb0, ret); goto err_unmap_io; } #endif init_waitqueue_head(&dev->netinfo_waitq); dev->deliver_netinfo = 0; dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev, "dim2_netinfo"); if (IS_ERR(dev->netinfo_task)) { ret = PTR_ERR(dev->netinfo_task); goto err_free_irq; } for (i = 0; i < DMA_CHANNELS; i++) { struct most_channel_capability *cap = dev->capabilities + i; struct hdm_channel *hdm_ch = dev->hch + i; INIT_LIST_HEAD(&hdm_ch->pending_list); INIT_LIST_HEAD(&hdm_ch->started_list); hdm_ch->is_initialized = false; snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2); cap->name_suffix = hdm_ch->name; cap->direction = MOST_CH_RX | MOST_CH_TX; cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | MOST_CH_ISOC_AVP | MOST_CH_SYNC; cap->num_buffers_packet = MAX_BUFFERS_PACKET; cap->buffer_size_packet = MAX_BUF_SIZE_PACKET; cap->num_buffers_streaming = MAX_BUFFERS_STREAMING; cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING; } { const char *fmt; if (sizeof(res->start) == sizeof(long long)) fmt = "dim2-%016llx"; else if (sizeof(res->start) == sizeof(long)) fmt = "dim2-%016lx"; else fmt = "dim2-%016x"; snprintf(dev->name, sizeof(dev->name), fmt, res->start); } dev->most_iface.interface = ITYPE_MEDIALB_DIM2; dev->most_iface.description = dev->name; dev->most_iface.num_channels = DMA_CHANNELS; dev->most_iface.channel_vector = dev->capabilities; dev->most_iface.configure = configure_channel; dev->most_iface.enqueue = enqueue; dev->most_iface.poison_channel = poison_channel; dev->most_iface.request_netinfo = request_netinfo; kobj = most_register_interface(&dev->most_iface); if (IS_ERR(kobj)) { ret = PTR_ERR(kobj); pr_err("failed to register MOST interface\n"); goto err_stop_thread; } ret = dim2_sysfs_probe(&dev->bus, kobj); if (ret) goto err_unreg_iface; ret = startup_dim(pdev); if (ret) { pr_err("failed to initialize DIM2\n"); goto err_destroy_bus; } return 0; err_destroy_bus: dim2_sysfs_destroy(&dev->bus); err_unreg_iface: most_deregister_interface(&dev->most_iface); err_stop_thread: kthread_stop(dev->netinfo_task); err_free_irq: #if !defined(ENABLE_HDM_TEST) free_irq(dev->irq_ahb0, dev); err_unmap_io: iounmap(dev->io_base); err_release_mem: release_mem_region(res->start, resource_size(res)); err_free_dev: #endif kfree(dev); return ret; } /** * dim2_remove - dim2 remove handler * @pdev: platform device structure * * Unregister the interface from mostcore */ static int dim2_remove(struct platform_device *pdev) { struct dim2_hdm *dev = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct dim2_platform_data *pdata = pdev->dev.platform_data; unsigned long flags; spin_lock_irqsave(&dim_lock, flags); dim_shutdown(); spin_unlock_irqrestore(&dim_lock, flags); if (pdata && pdata->destroy) pdata->destroy(pdata); dim2_sysfs_destroy(&dev->bus); most_deregister_interface(&dev->most_iface); kthread_stop(dev->netinfo_task); #if !defined(ENABLE_HDM_TEST) free_irq(dev->irq_ahb0, dev); iounmap(dev->io_base); release_mem_region(res->start, resource_size(res)); #endif kfree(dev); platform_set_drvdata(pdev, NULL); /* * break link to local platform_device_id struct * to prevent crash by unload platform device module */ pdev->id_entry = NULL; return 0; } static struct platform_device_id dim2_id[] = { { "medialb_dim2" }, { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(platform, dim2_id); static struct platform_driver dim2_driver = { .probe = dim2_probe, .remove = dim2_remove, .id_table = dim2_id, .driver = { .name = "hdm_dim2", }, }; module_platform_driver(dim2_driver); MODULE_AUTHOR("Jain Roy Ambi <JainRoy.Ambi@microchip.com>"); MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>"); MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module"); MODULE_LICENSE("GPL");