~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/samples/qmi/qmi_sample_client.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Sample in-kernel QMI client driver
  4  *
  5  * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  6  * Copyright (C) 2017 Linaro Ltd.
  7  */
  8 #include <linux/kernel.h>
  9 #include <linux/module.h>
 10 #include <linux/debugfs.h>
 11 #include <linux/device.h>
 12 #include <linux/platform_device.h>
 13 #include <linux/qrtr.h>
 14 #include <linux/net.h>
 15 #include <linux/completion.h>
 16 #include <linux/idr.h>
 17 #include <linux/string.h>
 18 #include <net/sock.h>
 19 #include <linux/soc/qcom/qmi.h>
 20 
 21 #define PING_REQ1_TLV_TYPE              0x1
 22 #define PING_RESP1_TLV_TYPE             0x2
 23 #define PING_OPT1_TLV_TYPE              0x10
 24 #define PING_OPT2_TLV_TYPE              0x11
 25 
 26 #define DATA_REQ1_TLV_TYPE              0x1
 27 #define DATA_RESP1_TLV_TYPE             0x2
 28 #define DATA_OPT1_TLV_TYPE              0x10
 29 #define DATA_OPT2_TLV_TYPE              0x11
 30 
 31 #define TEST_MED_DATA_SIZE_V01          8192
 32 #define TEST_MAX_NAME_SIZE_V01          255
 33 
 34 #define TEST_PING_REQ_MSG_ID_V01        0x20
 35 #define TEST_DATA_REQ_MSG_ID_V01        0x21
 36 
 37 #define TEST_PING_REQ_MAX_MSG_LEN_V01   266
 38 #define TEST_DATA_REQ_MAX_MSG_LEN_V01   8456
 39 
 40 struct test_name_type_v01 {
 41         u32 name_len;
 42         char name[TEST_MAX_NAME_SIZE_V01];
 43 };
 44 
 45 static const struct qmi_elem_info test_name_type_v01_ei[] = {
 46         {
 47                 .data_type      = QMI_DATA_LEN,
 48                 .elem_len       = 1,
 49                 .elem_size      = sizeof(u8),
 50                 .array_type     = NO_ARRAY,
 51                 .tlv_type       = QMI_COMMON_TLV_TYPE,
 52                 .offset         = offsetof(struct test_name_type_v01,
 53                                            name_len),
 54         },
 55         {
 56                 .data_type      = QMI_UNSIGNED_1_BYTE,
 57                 .elem_len       = TEST_MAX_NAME_SIZE_V01,
 58                 .elem_size      = sizeof(char),
 59                 .array_type     = VAR_LEN_ARRAY,
 60                 .tlv_type       = QMI_COMMON_TLV_TYPE,
 61                 .offset         = offsetof(struct test_name_type_v01,
 62                                            name),
 63         },
 64         {}
 65 };
 66 
 67 struct test_ping_req_msg_v01 {
 68         char ping[4];
 69 
 70         u8 client_name_valid;
 71         struct test_name_type_v01 client_name;
 72 };
 73 
 74 static const struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
 75         {
 76                 .data_type      = QMI_UNSIGNED_1_BYTE,
 77                 .elem_len       = 4,
 78                 .elem_size      = sizeof(char),
 79                 .array_type     = STATIC_ARRAY,
 80                 .tlv_type       = PING_REQ1_TLV_TYPE,
 81                 .offset         = offsetof(struct test_ping_req_msg_v01,
 82                                            ping),
 83         },
 84         {
 85                 .data_type      = QMI_OPT_FLAG,
 86                 .elem_len       = 1,
 87                 .elem_size      = sizeof(u8),
 88                 .array_type     = NO_ARRAY,
 89                 .tlv_type       = PING_OPT1_TLV_TYPE,
 90                 .offset         = offsetof(struct test_ping_req_msg_v01,
 91                                            client_name_valid),
 92         },
 93         {
 94                 .data_type      = QMI_STRUCT,
 95                 .elem_len       = 1,
 96                 .elem_size      = sizeof(struct test_name_type_v01),
 97                 .array_type     = NO_ARRAY,
 98                 .tlv_type       = PING_OPT1_TLV_TYPE,
 99                 .offset         = offsetof(struct test_ping_req_msg_v01,
100                                            client_name),
101                 .ei_array       = test_name_type_v01_ei,
102         },
103         {}
104 };
105 
106 struct test_ping_resp_msg_v01 {
107         struct qmi_response_type_v01 resp;
108 
109         u8 pong_valid;
110         char pong[4];
111 
112         u8 service_name_valid;
113         struct test_name_type_v01 service_name;
114 };
115 
116 static const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
117         {
118                 .data_type      = QMI_STRUCT,
119                 .elem_len       = 1,
120                 .elem_size      = sizeof(struct qmi_response_type_v01),
121                 .array_type     = NO_ARRAY,
122                 .tlv_type       = PING_RESP1_TLV_TYPE,
123                 .offset         = offsetof(struct test_ping_resp_msg_v01,
124                                            resp),
125                 .ei_array       = qmi_response_type_v01_ei,
126         },
127         {
128                 .data_type      = QMI_OPT_FLAG,
129                 .elem_len       = 1,
130                 .elem_size      = sizeof(u8),
131                 .array_type     = NO_ARRAY,
132                 .tlv_type       = PING_OPT1_TLV_TYPE,
133                 .offset         = offsetof(struct test_ping_resp_msg_v01,
134                                            pong_valid),
135         },
136         {
137                 .data_type      = QMI_UNSIGNED_1_BYTE,
138                 .elem_len       = 4,
139                 .elem_size      = sizeof(char),
140                 .array_type     = STATIC_ARRAY,
141                 .tlv_type       = PING_OPT1_TLV_TYPE,
142                 .offset         = offsetof(struct test_ping_resp_msg_v01,
143                                            pong),
144         },
145         {
146                 .data_type      = QMI_OPT_FLAG,
147                 .elem_len       = 1,
148                 .elem_size      = sizeof(u8),
149                 .array_type     = NO_ARRAY,
150                 .tlv_type       = PING_OPT2_TLV_TYPE,
151                 .offset         = offsetof(struct test_ping_resp_msg_v01,
152                                            service_name_valid),
153         },
154         {
155                 .data_type      = QMI_STRUCT,
156                 .elem_len       = 1,
157                 .elem_size      = sizeof(struct test_name_type_v01),
158                 .array_type     = NO_ARRAY,
159                 .tlv_type       = PING_OPT2_TLV_TYPE,
160                 .offset         = offsetof(struct test_ping_resp_msg_v01,
161                                            service_name),
162                 .ei_array       = test_name_type_v01_ei,
163         },
164         {}
165 };
166 
167 struct test_data_req_msg_v01 {
168         u32 data_len;
169         u8 data[TEST_MED_DATA_SIZE_V01];
170 
171         u8 client_name_valid;
172         struct test_name_type_v01 client_name;
173 };
174 
175 static const struct qmi_elem_info test_data_req_msg_v01_ei[] = {
176         {
177                 .data_type      = QMI_DATA_LEN,
178                 .elem_len       = 1,
179                 .elem_size      = sizeof(u32),
180                 .array_type     = NO_ARRAY,
181                 .tlv_type       = DATA_REQ1_TLV_TYPE,
182                 .offset         = offsetof(struct test_data_req_msg_v01,
183                                            data_len),
184         },
185         {
186                 .data_type      = QMI_UNSIGNED_1_BYTE,
187                 .elem_len       = TEST_MED_DATA_SIZE_V01,
188                 .elem_size      = sizeof(u8),
189                 .array_type     = VAR_LEN_ARRAY,
190                 .tlv_type       = DATA_REQ1_TLV_TYPE,
191                 .offset         = offsetof(struct test_data_req_msg_v01,
192                                            data),
193         },
194         {
195                 .data_type      = QMI_OPT_FLAG,
196                 .elem_len       = 1,
197                 .elem_size      = sizeof(u8),
198                 .array_type     = NO_ARRAY,
199                 .tlv_type       = DATA_OPT1_TLV_TYPE,
200                 .offset         = offsetof(struct test_data_req_msg_v01,
201                                            client_name_valid),
202         },
203         {
204                 .data_type      = QMI_STRUCT,
205                 .elem_len       = 1,
206                 .elem_size      = sizeof(struct test_name_type_v01),
207                 .array_type     = NO_ARRAY,
208                 .tlv_type       = DATA_OPT1_TLV_TYPE,
209                 .offset         = offsetof(struct test_data_req_msg_v01,
210                                            client_name),
211                 .ei_array       = test_name_type_v01_ei,
212         },
213         {}
214 };
215 
216 struct test_data_resp_msg_v01 {
217         struct qmi_response_type_v01 resp;
218 
219         u8 data_valid;
220         u32 data_len;
221         u8 data[TEST_MED_DATA_SIZE_V01];
222 
223         u8 service_name_valid;
224         struct test_name_type_v01 service_name;
225 };
226 
227 static const struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
228         {
229                 .data_type      = QMI_STRUCT,
230                 .elem_len       = 1,
231                 .elem_size      = sizeof(struct qmi_response_type_v01),
232                 .array_type     = NO_ARRAY,
233                 .tlv_type       = DATA_RESP1_TLV_TYPE,
234                 .offset         = offsetof(struct test_data_resp_msg_v01,
235                                            resp),
236                 .ei_array       = qmi_response_type_v01_ei,
237         },
238         {
239                 .data_type      = QMI_OPT_FLAG,
240                 .elem_len       = 1,
241                 .elem_size      = sizeof(u8),
242                 .array_type     = NO_ARRAY,
243                 .tlv_type       = DATA_OPT1_TLV_TYPE,
244                 .offset         = offsetof(struct test_data_resp_msg_v01,
245                                            data_valid),
246         },
247         {
248                 .data_type      = QMI_DATA_LEN,
249                 .elem_len       = 1,
250                 .elem_size      = sizeof(u32),
251                 .array_type     = NO_ARRAY,
252                 .tlv_type       = DATA_OPT1_TLV_TYPE,
253                 .offset         = offsetof(struct test_data_resp_msg_v01,
254                                            data_len),
255         },
256         {
257                 .data_type      = QMI_UNSIGNED_1_BYTE,
258                 .elem_len       = TEST_MED_DATA_SIZE_V01,
259                 .elem_size      = sizeof(u8),
260                 .array_type     = VAR_LEN_ARRAY,
261                 .tlv_type       = DATA_OPT1_TLV_TYPE,
262                 .offset         = offsetof(struct test_data_resp_msg_v01,
263                                            data),
264         },
265         {
266                 .data_type      = QMI_OPT_FLAG,
267                 .elem_len       = 1,
268                 .elem_size      = sizeof(u8),
269                 .array_type     = NO_ARRAY,
270                 .tlv_type       = DATA_OPT2_TLV_TYPE,
271                 .offset         = offsetof(struct test_data_resp_msg_v01,
272                                            service_name_valid),
273         },
274         {
275                 .data_type      = QMI_STRUCT,
276                 .elem_len       = 1,
277                 .elem_size      = sizeof(struct test_name_type_v01),
278                 .array_type     = NO_ARRAY,
279                 .tlv_type       = DATA_OPT2_TLV_TYPE,
280                 .offset         = offsetof(struct test_data_resp_msg_v01,
281                                            service_name),
282                 .ei_array       = test_name_type_v01_ei,
283         },
284         {}
285 };
286 
287 /*
288  * ping_write() - ping_pong debugfs file write handler
289  * @file:       debugfs file context
290  * @user_buf:   reference to the user data (ignored)
291  * @count:      number of bytes in @user_buf
292  * @ppos:       offset in @file to write
293  *
294  * This function allows user space to send out a ping_pong QMI encoded message
295  * to the associated remote test service and will return with the result of the
296  * transaction. It serves as an example of how to provide a custom response
297  * handler.
298  *
299  * Return: @count, or negative errno on failure.
300  */
301 static ssize_t ping_write(struct file *file, const char __user *user_buf,
302                           size_t count, loff_t *ppos)
303 {
304         struct qmi_handle *qmi = file->private_data;
305         struct test_ping_req_msg_v01 req = {};
306         struct qmi_txn txn;
307         int ret;
308 
309         memcpy(req.ping, "ping", sizeof(req.ping));
310 
311         ret = qmi_txn_init(qmi, &txn, NULL, NULL);
312         if (ret < 0)
313                 return ret;
314 
315         ret = qmi_send_request(qmi, NULL, &txn,
316                                TEST_PING_REQ_MSG_ID_V01,
317                                TEST_PING_REQ_MAX_MSG_LEN_V01,
318                                test_ping_req_msg_v01_ei, &req);
319         if (ret < 0) {
320                 qmi_txn_cancel(&txn);
321                 return ret;
322         }
323 
324         ret = qmi_txn_wait(&txn, 5 * HZ);
325         if (ret < 0)
326                 count = ret;
327 
328         return count;
329 }
330 
331 static const struct file_operations ping_fops = {
332         .open = simple_open,
333         .write = ping_write,
334 };
335 
336 static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
337                          struct qmi_txn *txn, const void *data)
338 {
339         const struct test_ping_resp_msg_v01 *resp = data;
340 
341         if (!txn) {
342                 pr_err("spurious ping response\n");
343                 return;
344         }
345 
346         if (resp->resp.result == QMI_RESULT_FAILURE_V01)
347                 txn->result = -ENXIO;
348         else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
349                 txn->result = -EINVAL;
350 
351         complete(&txn->completion);
352 }
353 
354 /*
355  * data_write() - data debugfs file write handler
356  * @file:       debugfs file context
357  * @user_buf:   reference to the user data
358  * @count:      number of bytes in @user_buf
359  * @ppos:       offset in @file to write
360  *
361  * This function allows user space to send out a data QMI encoded message to
362  * the associated remote test service and will return with the result of the
363  * transaction. It serves as an example of how to have the QMI helpers decode a
364  * transaction response into a provided object automatically.
365  *
366  * Return: @count, or negative errno on failure.
367  */
368 static ssize_t data_write(struct file *file, const char __user *user_buf,
369                           size_t count, loff_t *ppos)
370 
371 {
372         struct qmi_handle *qmi = file->private_data;
373         struct test_data_resp_msg_v01 *resp;
374         struct test_data_req_msg_v01 *req;
375         struct qmi_txn txn;
376         int ret;
377 
378         req = kzalloc(sizeof(*req), GFP_KERNEL);
379         if (!req)
380                 return -ENOMEM;
381 
382         resp = kzalloc(sizeof(*resp), GFP_KERNEL);
383         if (!resp) {
384                 kfree(req);
385                 return -ENOMEM;
386         }
387 
388         req->data_len = min_t(size_t, sizeof(req->data), count);
389         if (copy_from_user(req->data, user_buf, req->data_len)) {
390                 ret = -EFAULT;
391                 goto out;
392         }
393 
394         ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
395         if (ret < 0)
396                 goto out;
397 
398         ret = qmi_send_request(qmi, NULL, &txn,
399                                TEST_DATA_REQ_MSG_ID_V01,
400                                TEST_DATA_REQ_MAX_MSG_LEN_V01,
401                                test_data_req_msg_v01_ei, req);
402         if (ret < 0) {
403                 qmi_txn_cancel(&txn);
404                 goto out;
405         }
406 
407         ret = qmi_txn_wait(&txn, 5 * HZ);
408         if (ret < 0) {
409                 goto out;
410         } else if (!resp->data_valid ||
411                    resp->data_len != req->data_len ||
412                    memcmp(resp->data, req->data, req->data_len)) {
413                 pr_err("response data doesn't match expectation\n");
414                 ret = -EINVAL;
415                 goto out;
416         }
417 
418         ret = count;
419 
420 out:
421         kfree(resp);
422         kfree(req);
423 
424         return ret;
425 }
426 
427 static const struct file_operations data_fops = {
428         .open = simple_open,
429         .write = data_write,
430 };
431 
432 static const struct qmi_msg_handler qmi_sample_handlers[] = {
433         {
434                 .type = QMI_RESPONSE,
435                 .msg_id = TEST_PING_REQ_MSG_ID_V01,
436                 .ei = test_ping_resp_msg_v01_ei,
437                 .decoded_size = sizeof(struct test_ping_req_msg_v01),
438                 .fn = ping_pong_cb
439         },
440         {}
441 };
442 
443 struct qmi_sample {
444         struct qmi_handle qmi;
445 
446         struct dentry *de_dir;
447         struct dentry *de_data;
448         struct dentry *de_ping;
449 };
450 
451 static struct dentry *qmi_debug_dir;
452 
453 static int qmi_sample_probe(struct platform_device *pdev)
454 {
455         struct sockaddr_qrtr *sq;
456         struct qmi_sample *sample;
457         char path[20];
458         int ret;
459 
460         sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
461         if (!sample)
462                 return -ENOMEM;
463 
464         ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
465                               NULL,
466                               qmi_sample_handlers);
467         if (ret < 0)
468                 return ret;
469 
470         sq = dev_get_platdata(&pdev->dev);
471         ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
472                              sizeof(*sq), 0);
473         if (ret < 0) {
474                 pr_err("failed to connect to remote service port\n");
475                 goto err_release_qmi_handle;
476         }
477 
478         snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
479 
480         sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
481         if (IS_ERR(sample->de_dir)) {
482                 ret = PTR_ERR(sample->de_dir);
483                 goto err_release_qmi_handle;
484         }
485 
486         sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
487                                               sample, &data_fops);
488         if (IS_ERR(sample->de_data)) {
489                 ret = PTR_ERR(sample->de_data);
490                 goto err_remove_de_dir;
491         }
492 
493         sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
494                                               sample, &ping_fops);
495         if (IS_ERR(sample->de_ping)) {
496                 ret = PTR_ERR(sample->de_ping);
497                 goto err_remove_de_data;
498         }
499 
500         platform_set_drvdata(pdev, sample);
501 
502         return 0;
503 
504 err_remove_de_data:
505         debugfs_remove(sample->de_data);
506 err_remove_de_dir:
507         debugfs_remove(sample->de_dir);
508 err_release_qmi_handle:
509         qmi_handle_release(&sample->qmi);
510 
511         return ret;
512 }
513 
514 static void qmi_sample_remove(struct platform_device *pdev)
515 {
516         struct qmi_sample *sample = platform_get_drvdata(pdev);
517 
518         debugfs_remove(sample->de_ping);
519         debugfs_remove(sample->de_data);
520         debugfs_remove(sample->de_dir);
521 
522         qmi_handle_release(&sample->qmi);
523 }
524 
525 static struct platform_driver qmi_sample_driver = {
526         .probe = qmi_sample_probe,
527         .remove_new = qmi_sample_remove,
528         .driver = {
529                 .name = "qmi_sample_client",
530         },
531 };
532 
533 static int qmi_sample_new_server(struct qmi_handle *qmi,
534                                  struct qmi_service *service)
535 {
536         struct platform_device *pdev;
537         struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
538         int ret;
539 
540         pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
541         if (!pdev)
542                 return -ENOMEM;
543 
544         ret = platform_device_add_data(pdev, &sq, sizeof(sq));
545         if (ret)
546                 goto err_put_device;
547 
548         ret = platform_device_add(pdev);
549         if (ret)
550                 goto err_put_device;
551 
552         service->priv = pdev;
553 
554         return 0;
555 
556 err_put_device:
557         platform_device_put(pdev);
558 
559         return ret;
560 }
561 
562 static void qmi_sample_del_server(struct qmi_handle *qmi,
563                                   struct qmi_service *service)
564 {
565         struct platform_device *pdev = service->priv;
566 
567         platform_device_unregister(pdev);
568 }
569 
570 static struct qmi_handle lookup_client;
571 
572 static const struct qmi_ops lookup_ops = {
573         .new_server = qmi_sample_new_server,
574         .del_server = qmi_sample_del_server,
575 };
576 
577 static int qmi_sample_init(void)
578 {
579         int ret;
580 
581         qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
582         if (IS_ERR(qmi_debug_dir)) {
583                 pr_err("failed to create qmi_sample dir\n");
584                 return PTR_ERR(qmi_debug_dir);
585         }
586 
587         ret = platform_driver_register(&qmi_sample_driver);
588         if (ret)
589                 goto err_remove_debug_dir;
590 
591         ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
592         if (ret < 0)
593                 goto err_unregister_driver;
594 
595         qmi_add_lookup(&lookup_client, 15, 0, 0);
596 
597         return 0;
598 
599 err_unregister_driver:
600         platform_driver_unregister(&qmi_sample_driver);
601 err_remove_debug_dir:
602         debugfs_remove(qmi_debug_dir);
603 
604         return ret;
605 }
606 
607 static void qmi_sample_exit(void)
608 {
609         qmi_handle_release(&lookup_client);
610 
611         platform_driver_unregister(&qmi_sample_driver);
612 
613         debugfs_remove(qmi_debug_dir);
614 }
615 
616 module_init(qmi_sample_init);
617 module_exit(qmi_sample_exit);
618 
619 MODULE_DESCRIPTION("Sample QMI client driver");
620 MODULE_LICENSE("GPL v2");
621 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php