1
2
3
4
5
6#include <linux/backlight.h>
7#include <linux/workqueue.h>
8
9#include <drm/drm_atomic.h>
10#include <drm/drm_atomic_state_helper.h>
11#include <drm/drm_connector.h>
12#include <drm/drm_drv.h>
13#include <drm/drm_encoder.h>
14#include <drm/drm_file.h>
15#include <drm/drm_modeset_helper_vtables.h>
16#include <drm/drm_print.h>
17#include <drm/drm_probe_helper.h>
18#include <drm/drm_simple_kms_helper.h>
19#include <drm/gud.h>
20
21#include "gud_internal.h"
22
23struct gud_connector {
24 struct drm_connector connector;
25 struct drm_encoder encoder;
26 struct backlight_device *backlight;
27 struct work_struct backlight_work;
28
29
30 u16 *properties;
31 unsigned int num_properties;
32
33
34 struct drm_tv_connector_state initial_tv_state;
35
36
37
38
39
40 int initial_brightness;
41};
42
43static inline struct gud_connector *to_gud_connector(struct drm_connector *connector)
44{
45 return container_of(connector, struct gud_connector, connector);
46}
47
48static void gud_conn_err(struct drm_connector *connector, const char *msg, int ret)
49{
50 dev_err(connector->dev->dev, "%s: %s (ret=%d)\n", connector->name, msg, ret);
51}
52
53
54
55
56
57
58static void gud_connector_backlight_update_status_work(struct work_struct *work)
59{
60 struct gud_connector *gconn = container_of(work, struct gud_connector, backlight_work);
61 struct drm_connector *connector = &gconn->connector;
62 struct drm_connector_state *connector_state;
63 struct drm_device *drm = connector->dev;
64 struct drm_modeset_acquire_ctx ctx;
65 struct drm_atomic_state *state;
66 int idx, ret;
67
68 if (!drm_dev_enter(drm, &idx))
69 return;
70
71 state = drm_atomic_state_alloc(drm);
72 if (!state) {
73 ret = -ENOMEM;
74 goto exit;
75 }
76
77 drm_modeset_acquire_init(&ctx, 0);
78 state->acquire_ctx = &ctx;
79retry:
80 connector_state = drm_atomic_get_connector_state(state, connector);
81 if (IS_ERR(connector_state)) {
82 ret = PTR_ERR(connector_state);
83 goto out;
84 }
85
86
87 connector_state->tv.brightness = gconn->backlight->props.brightness;
88
89 ret = drm_atomic_commit(state);
90out:
91 if (ret == -EDEADLK) {
92 drm_atomic_state_clear(state);
93 drm_modeset_backoff(&ctx);
94 goto retry;
95 }
96
97 drm_atomic_state_put(state);
98
99 drm_modeset_drop_locks(&ctx);
100 drm_modeset_acquire_fini(&ctx);
101exit:
102 drm_dev_exit(idx);
103
104 if (ret)
105 dev_err(drm->dev, "Failed to update backlight, err=%d\n", ret);
106}
107
108static int gud_connector_backlight_update_status(struct backlight_device *bd)
109{
110 struct drm_connector *connector = bl_get_data(bd);
111 struct gud_connector *gconn = to_gud_connector(connector);
112
113
114 queue_work(system_long_wq, &gconn->backlight_work);
115
116 return 0;
117}
118
119static const struct backlight_ops gud_connector_backlight_ops = {
120 .update_status = gud_connector_backlight_update_status,
121};
122
123static int gud_connector_backlight_register(struct gud_connector *gconn)
124{
125 struct drm_connector *connector = &gconn->connector;
126 struct backlight_device *bd;
127 const char *name;
128 const struct backlight_properties props = {
129 .type = BACKLIGHT_RAW,
130 .scale = BACKLIGHT_SCALE_NON_LINEAR,
131 .max_brightness = 100,
132 .brightness = gconn->initial_brightness,
133 };
134
135 name = kasprintf(GFP_KERNEL, "card%d-%s-backlight",
136 connector->dev->primary->index, connector->name);
137 if (!name)
138 return -ENOMEM;
139
140 bd = backlight_device_register(name, connector->kdev, connector,
141 &gud_connector_backlight_ops, &props);
142 kfree(name);
143 if (IS_ERR(bd))
144 return PTR_ERR(bd);
145
146 gconn->backlight = bd;
147
148 return 0;
149}
150
151static int gud_connector_detect(struct drm_connector *connector,
152 struct drm_modeset_acquire_ctx *ctx, bool force)
153{
154 struct gud_device *gdrm = to_gud_device(connector->dev);
155 int idx, ret;
156 u8 status;
157
158 if (!drm_dev_enter(connector->dev, &idx))
159 return connector_status_disconnected;
160
161 if (force) {
162 ret = gud_usb_set(gdrm, GUD_REQ_SET_CONNECTOR_FORCE_DETECT,
163 connector->index, NULL, 0);
164 if (ret) {
165 ret = connector_status_unknown;
166 goto exit;
167 }
168 }
169
170 ret = gud_usb_get_u8(gdrm, GUD_REQ_GET_CONNECTOR_STATUS, connector->index, &status);
171 if (ret) {
172 ret = connector_status_unknown;
173 goto exit;
174 }
175
176 switch (status & GUD_CONNECTOR_STATUS_CONNECTED_MASK) {
177 case GUD_CONNECTOR_STATUS_DISCONNECTED:
178 ret = connector_status_disconnected;
179 break;
180 case GUD_CONNECTOR_STATUS_CONNECTED:
181 ret = connector_status_connected;
182 break;
183 default:
184 ret = connector_status_unknown;
185 break;
186 }
187
188 if (status & GUD_CONNECTOR_STATUS_CHANGED)
189 connector->epoch_counter += 1;
190exit:
191 drm_dev_exit(idx);
192
193 return ret;
194}
195
196struct gud_connector_get_edid_ctx {
197 void *buf;
198 size_t len;
199 bool edid_override;
200};
201
202static int gud_connector_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
203{
204 struct gud_connector_get_edid_ctx *ctx = data;
205 size_t start = block * EDID_LENGTH;
206
207 ctx->edid_override = false;
208
209 if (start + len > ctx->len)
210 return -1;
211
212 memcpy(buf, ctx->buf + start, len);
213
214 return 0;
215}
216
217static int gud_connector_get_modes(struct drm_connector *connector)
218{
219 struct gud_device *gdrm = to_gud_device(connector->dev);
220 struct gud_display_mode_req *reqmodes = NULL;
221 struct gud_connector_get_edid_ctx edid_ctx;
222 unsigned int i, num_modes = 0;
223 struct edid *edid = NULL;
224 int idx, ret;
225
226 if (!drm_dev_enter(connector->dev, &idx))
227 return 0;
228
229 edid_ctx.edid_override = true;
230 edid_ctx.buf = kmalloc(GUD_CONNECTOR_MAX_EDID_LEN, GFP_KERNEL);
231 if (!edid_ctx.buf)
232 goto out;
233
234 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_EDID, connector->index,
235 edid_ctx.buf, GUD_CONNECTOR_MAX_EDID_LEN);
236 if (ret > 0 && ret % EDID_LENGTH) {
237 gud_conn_err(connector, "Invalid EDID size", ret);
238 } else if (ret > 0) {
239 edid_ctx.len = ret;
240 edid = drm_do_get_edid(connector, gud_connector_get_edid_block, &edid_ctx);
241 }
242
243 kfree(edid_ctx.buf);
244 drm_connector_update_edid_property(connector, edid);
245
246 if (edid && edid_ctx.edid_override)
247 goto out;
248
249 reqmodes = kmalloc_array(GUD_CONNECTOR_MAX_NUM_MODES, sizeof(*reqmodes), GFP_KERNEL);
250 if (!reqmodes)
251 goto out;
252
253 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_MODES, connector->index,
254 reqmodes, GUD_CONNECTOR_MAX_NUM_MODES * sizeof(*reqmodes));
255 if (ret <= 0)
256 goto out;
257 if (ret % sizeof(*reqmodes)) {
258 gud_conn_err(connector, "Invalid display mode array size", ret);
259 goto out;
260 }
261
262 num_modes = ret / sizeof(*reqmodes);
263
264 for (i = 0; i < num_modes; i++) {
265 struct drm_display_mode *mode;
266
267 mode = drm_mode_create(connector->dev);
268 if (!mode) {
269 num_modes = i;
270 goto out;
271 }
272
273 gud_to_display_mode(mode, &reqmodes[i]);
274 drm_mode_probed_add(connector, mode);
275 }
276out:
277 if (!num_modes)
278 num_modes = drm_add_edid_modes(connector, edid);
279
280 kfree(reqmodes);
281 kfree(edid);
282 drm_dev_exit(idx);
283
284 return num_modes;
285}
286
287static int gud_connector_atomic_check(struct drm_connector *connector,
288 struct drm_atomic_state *state)
289{
290 struct drm_connector_state *new_state;
291 struct drm_crtc_state *new_crtc_state;
292 struct drm_connector_state *old_state;
293
294 new_state = drm_atomic_get_new_connector_state(state, connector);
295 if (!new_state->crtc)
296 return 0;
297
298 old_state = drm_atomic_get_old_connector_state(state, connector);
299 new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
300
301 if (old_state->tv.margins.left != new_state->tv.margins.left ||
302 old_state->tv.margins.right != new_state->tv.margins.right ||
303 old_state->tv.margins.top != new_state->tv.margins.top ||
304 old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
305 old_state->tv.mode != new_state->tv.mode ||
306 old_state->tv.brightness != new_state->tv.brightness ||
307 old_state->tv.contrast != new_state->tv.contrast ||
308 old_state->tv.flicker_reduction != new_state->tv.flicker_reduction ||
309 old_state->tv.overscan != new_state->tv.overscan ||
310 old_state->tv.saturation != new_state->tv.saturation ||
311 old_state->tv.hue != new_state->tv.hue)
312 new_crtc_state->connectors_changed = true;
313
314 return 0;
315}
316
317static const struct drm_connector_helper_funcs gud_connector_helper_funcs = {
318 .detect_ctx = gud_connector_detect,
319 .get_modes = gud_connector_get_modes,
320 .atomic_check = gud_connector_atomic_check,
321};
322
323static int gud_connector_late_register(struct drm_connector *connector)
324{
325 struct gud_connector *gconn = to_gud_connector(connector);
326
327 if (gconn->initial_brightness < 0)
328 return 0;
329
330 return gud_connector_backlight_register(gconn);
331}
332
333static void gud_connector_early_unregister(struct drm_connector *connector)
334{
335 struct gud_connector *gconn = to_gud_connector(connector);
336
337 backlight_device_unregister(gconn->backlight);
338 cancel_work_sync(&gconn->backlight_work);
339}
340
341static void gud_connector_destroy(struct drm_connector *connector)
342{
343 struct gud_connector *gconn = to_gud_connector(connector);
344
345 drm_connector_cleanup(connector);
346 kfree(gconn->properties);
347 kfree(gconn);
348}
349
350static void gud_connector_reset(struct drm_connector *connector)
351{
352 struct gud_connector *gconn = to_gud_connector(connector);
353
354 drm_atomic_helper_connector_reset(connector);
355 connector->state->tv = gconn->initial_tv_state;
356
357 drm_atomic_helper_connector_tv_reset(connector);
358 if (gconn->initial_brightness >= 0)
359 connector->state->tv.brightness = gconn->initial_brightness;
360}
361
362static const struct drm_connector_funcs gud_connector_funcs = {
363 .fill_modes = drm_helper_probe_single_connector_modes,
364 .late_register = gud_connector_late_register,
365 .early_unregister = gud_connector_early_unregister,
366 .destroy = gud_connector_destroy,
367 .reset = gud_connector_reset,
368 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
369 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
370};
371
372
373
374
375
376
377static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connector *connector)
378{
379 size_t buf_len = GUD_CONNECTOR_TV_MODE_MAX_NUM * GUD_CONNECTOR_TV_MODE_NAME_LEN;
380 const char *modes[GUD_CONNECTOR_TV_MODE_MAX_NUM];
381 unsigned int i, num_modes;
382 char *buf;
383 int ret;
384
385 buf = kmalloc(buf_len, GFP_KERNEL);
386 if (!buf)
387 return -ENOMEM;
388
389 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_TV_MODE_VALUES,
390 connector->index, buf, buf_len);
391 if (ret < 0)
392 goto free;
393 if (!ret || ret % GUD_CONNECTOR_TV_MODE_NAME_LEN) {
394 ret = -EIO;
395 goto free;
396 }
397
398 num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
399 for (i = 0; i < num_modes; i++)
400 modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
401
402 ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
403free:
404 kfree(buf);
405 if (ret < 0)
406 gud_conn_err(connector, "Failed to add TV modes", ret);
407
408 return ret;
409}
410
411static struct drm_property *
412gud_connector_property_lookup(struct drm_connector *connector, u16 prop)
413{
414 struct drm_mode_config *config = &connector->dev->mode_config;
415
416 switch (prop) {
417 case GUD_PROPERTY_TV_LEFT_MARGIN:
418 return config->tv_left_margin_property;
419 case GUD_PROPERTY_TV_RIGHT_MARGIN:
420 return config->tv_right_margin_property;
421 case GUD_PROPERTY_TV_TOP_MARGIN:
422 return config->tv_top_margin_property;
423 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
424 return config->tv_bottom_margin_property;
425 case GUD_PROPERTY_TV_MODE:
426 return config->tv_mode_property;
427 case GUD_PROPERTY_TV_BRIGHTNESS:
428 return config->tv_brightness_property;
429 case GUD_PROPERTY_TV_CONTRAST:
430 return config->tv_contrast_property;
431 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
432 return config->tv_flicker_reduction_property;
433 case GUD_PROPERTY_TV_OVERSCAN:
434 return config->tv_overscan_property;
435 case GUD_PROPERTY_TV_SATURATION:
436 return config->tv_saturation_property;
437 case GUD_PROPERTY_TV_HUE:
438 return config->tv_hue_property;
439 default:
440 return ERR_PTR(-EINVAL);
441 }
442}
443
444static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connector_state *state)
445{
446 switch (prop) {
447 case GUD_PROPERTY_TV_LEFT_MARGIN:
448 return &state->margins.left;
449 case GUD_PROPERTY_TV_RIGHT_MARGIN:
450 return &state->margins.right;
451 case GUD_PROPERTY_TV_TOP_MARGIN:
452 return &state->margins.top;
453 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
454 return &state->margins.bottom;
455 case GUD_PROPERTY_TV_MODE:
456 return &state->mode;
457 case GUD_PROPERTY_TV_BRIGHTNESS:
458 return &state->brightness;
459 case GUD_PROPERTY_TV_CONTRAST:
460 return &state->contrast;
461 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
462 return &state->flicker_reduction;
463 case GUD_PROPERTY_TV_OVERSCAN:
464 return &state->overscan;
465 case GUD_PROPERTY_TV_SATURATION:
466 return &state->saturation;
467 case GUD_PROPERTY_TV_HUE:
468 return &state->hue;
469 default:
470 return ERR_PTR(-EINVAL);
471 }
472}
473
474static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_connector *gconn)
475{
476 struct drm_connector *connector = &gconn->connector;
477 struct drm_device *drm = &gdrm->drm;
478 struct gud_property_req *properties;
479 unsigned int i, num_properties;
480 int ret;
481
482 properties = kcalloc(GUD_CONNECTOR_PROPERTIES_MAX_NUM, sizeof(*properties), GFP_KERNEL);
483 if (!properties)
484 return -ENOMEM;
485
486 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTOR_PROPERTIES, connector->index,
487 properties, GUD_CONNECTOR_PROPERTIES_MAX_NUM * sizeof(*properties));
488 if (ret <= 0)
489 goto out;
490 if (ret % sizeof(*properties)) {
491 ret = -EIO;
492 goto out;
493 }
494
495 num_properties = ret / sizeof(*properties);
496 ret = 0;
497
498 gconn->properties = kcalloc(num_properties, sizeof(*gconn->properties), GFP_KERNEL);
499 if (!gconn->properties) {
500 ret = -ENOMEM;
501 goto out;
502 }
503
504 for (i = 0; i < num_properties; i++) {
505 u16 prop = le16_to_cpu(properties[i].prop);
506 u64 val = le64_to_cpu(properties[i].val);
507 struct drm_property *property;
508 unsigned int *state_val;
509
510 drm_dbg(drm, "property: %u = %llu(0x%llx)\n", prop, val, val);
511
512 switch (prop) {
513 case GUD_PROPERTY_TV_LEFT_MARGIN:
514 fallthrough;
515 case GUD_PROPERTY_TV_RIGHT_MARGIN:
516 fallthrough;
517 case GUD_PROPERTY_TV_TOP_MARGIN:
518 fallthrough;
519 case GUD_PROPERTY_TV_BOTTOM_MARGIN:
520 ret = drm_mode_create_tv_margin_properties(drm);
521 if (ret)
522 goto out;
523 break;
524 case GUD_PROPERTY_TV_MODE:
525 ret = gud_connector_add_tv_mode(gdrm, connector);
526 if (ret)
527 goto out;
528 break;
529 case GUD_PROPERTY_TV_BRIGHTNESS:
530 fallthrough;
531 case GUD_PROPERTY_TV_CONTRAST:
532 fallthrough;
533 case GUD_PROPERTY_TV_FLICKER_REDUCTION:
534 fallthrough;
535 case GUD_PROPERTY_TV_OVERSCAN:
536 fallthrough;
537 case GUD_PROPERTY_TV_SATURATION:
538 fallthrough;
539 case GUD_PROPERTY_TV_HUE:
540
541 ret = drm_mode_create_tv_properties(drm, 0, NULL);
542 if (ret)
543 goto out;
544 break;
545 case GUD_PROPERTY_BACKLIGHT_BRIGHTNESS:
546 if (val > 100) {
547 ret = -EINVAL;
548 goto out;
549 }
550 gconn->initial_brightness = val;
551 break;
552 default:
553
554 drm_dbg(drm, "Ignoring unknown property: %u\n", prop);
555 continue;
556 }
557
558 gconn->properties[gconn->num_properties++] = prop;
559
560 if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS)
561 continue;
562
563 property = gud_connector_property_lookup(connector, prop);
564 if (WARN_ON(IS_ERR(property)))
565 continue;
566
567 state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
568 if (WARN_ON(IS_ERR(state_val)))
569 continue;
570
571 *state_val = val;
572 drm_object_attach_property(&connector->base, property, 0);
573 }
574out:
575 kfree(properties);
576
577 return ret;
578}
579
580int gud_connector_fill_properties(struct drm_connector_state *connector_state,
581 struct gud_property_req *properties)
582{
583 struct gud_connector *gconn = to_gud_connector(connector_state->connector);
584 unsigned int i;
585
586 for (i = 0; i < gconn->num_properties; i++) {
587 u16 prop = gconn->properties[i];
588 u64 val;
589
590 if (prop == GUD_PROPERTY_BACKLIGHT_BRIGHTNESS) {
591 val = connector_state->tv.brightness;
592 } else {
593 unsigned int *state_val;
594
595 state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
596 if (WARN_ON_ONCE(IS_ERR(state_val)))
597 return PTR_ERR(state_val);
598
599 val = *state_val;
600 }
601
602 properties[i].prop = cpu_to_le16(prop);
603 properties[i].val = cpu_to_le64(val);
604 }
605
606 return gconn->num_properties;
607}
608
609static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
610 struct gud_connector_descriptor_req *desc)
611{
612 struct drm_device *drm = &gdrm->drm;
613 struct gud_connector *gconn;
614 struct drm_connector *connector;
615 struct drm_encoder *encoder;
616 int ret, connector_type;
617 u32 flags;
618
619 gconn = kzalloc(sizeof(*gconn), GFP_KERNEL);
620 if (!gconn)
621 return -ENOMEM;
622
623 INIT_WORK(&gconn->backlight_work, gud_connector_backlight_update_status_work);
624 gconn->initial_brightness = -ENODEV;
625 flags = le32_to_cpu(desc->flags);
626 connector = &gconn->connector;
627
628 drm_dbg(drm, "Connector: index=%u type=%u flags=0x%x\n", index, desc->connector_type, flags);
629
630 switch (desc->connector_type) {
631 case GUD_CONNECTOR_TYPE_PANEL:
632 connector_type = DRM_MODE_CONNECTOR_USB;
633 break;
634 case GUD_CONNECTOR_TYPE_VGA:
635 connector_type = DRM_MODE_CONNECTOR_VGA;
636 break;
637 case GUD_CONNECTOR_TYPE_DVI:
638 connector_type = DRM_MODE_CONNECTOR_DVID;
639 break;
640 case GUD_CONNECTOR_TYPE_COMPOSITE:
641 connector_type = DRM_MODE_CONNECTOR_Composite;
642 break;
643 case GUD_CONNECTOR_TYPE_SVIDEO:
644 connector_type = DRM_MODE_CONNECTOR_SVIDEO;
645 break;
646 case GUD_CONNECTOR_TYPE_COMPONENT:
647 connector_type = DRM_MODE_CONNECTOR_Component;
648 break;
649 case GUD_CONNECTOR_TYPE_DISPLAYPORT:
650 connector_type = DRM_MODE_CONNECTOR_DisplayPort;
651 break;
652 case GUD_CONNECTOR_TYPE_HDMI:
653 connector_type = DRM_MODE_CONNECTOR_HDMIA;
654 break;
655 default:
656 connector_type = DRM_MODE_CONNECTOR_USB;
657 break;
658 }
659
660 drm_connector_helper_add(connector, &gud_connector_helper_funcs);
661 ret = drm_connector_init(drm, connector, &gud_connector_funcs, connector_type);
662 if (ret) {
663 kfree(connector);
664 return ret;
665 }
666
667 if (WARN_ON(connector->index != index))
668 return -EINVAL;
669
670 if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
671 connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT);
672 if (flags & GUD_CONNECTOR_FLAGS_INTERLACE)
673 connector->interlace_allowed = true;
674 if (flags & GUD_CONNECTOR_FLAGS_DOUBLESCAN)
675 connector->doublescan_allowed = true;
676
677 ret = gud_connector_add_properties(gdrm, gconn);
678 if (ret) {
679 gud_conn_err(connector, "Failed to add properties", ret);
680 return ret;
681 }
682
683
684 if (!connector->index) {
685 encoder = &gdrm->pipe.encoder;
686 } else {
687 encoder = &gconn->encoder;
688
689 ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
690 if (ret)
691 return ret;
692
693 encoder->possible_crtcs = 1;
694 }
695
696 return drm_connector_attach_encoder(connector, encoder);
697}
698
699int gud_get_connectors(struct gud_device *gdrm)
700{
701 struct gud_connector_descriptor_req *descs;
702 unsigned int i, num_connectors;
703 int ret;
704
705 descs = kmalloc_array(GUD_CONNECTORS_MAX_NUM, sizeof(*descs), GFP_KERNEL);
706 if (!descs)
707 return -ENOMEM;
708
709 ret = gud_usb_get(gdrm, GUD_REQ_GET_CONNECTORS, 0,
710 descs, GUD_CONNECTORS_MAX_NUM * sizeof(*descs));
711 if (ret < 0)
712 goto free;
713 if (!ret || ret % sizeof(*descs)) {
714 ret = -EIO;
715 goto free;
716 }
717
718 num_connectors = ret / sizeof(*descs);
719
720 for (i = 0; i < num_connectors; i++) {
721 ret = gud_connector_create(gdrm, i, &descs[i]);
722 if (ret)
723 goto free;
724 }
725free:
726 kfree(descs);
727
728 return ret;
729}
730