1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> |
5 | */ |
6 | |
7 | #include <linux/string.h> |
8 | #include <linux/slab.h> |
9 | #include "pvrusb2-sysfs.h" |
10 | #include "pvrusb2-hdw.h" |
11 | #include "pvrusb2-debug.h" |
12 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
13 | #include "pvrusb2-debugifc.h" |
14 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
15 | |
16 | #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) |
17 | |
18 | struct pvr2_sysfs { |
19 | struct pvr2_channel channel; |
20 | struct device *class_dev; |
21 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
22 | struct pvr2_sysfs_debugifc *debugifc; |
23 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
24 | struct pvr2_sysfs_ctl_item *item_first; |
25 | struct pvr2_sysfs_ctl_item *item_last; |
26 | struct device_attribute attr_v4l_minor_number; |
27 | struct device_attribute attr_v4l_radio_minor_number; |
28 | struct device_attribute attr_unit_number; |
29 | struct device_attribute attr_bus_info; |
30 | struct device_attribute attr_hdw_name; |
31 | struct device_attribute attr_hdw_desc; |
32 | int v4l_minor_number_created_ok; |
33 | int v4l_radio_minor_number_created_ok; |
34 | int unit_number_created_ok; |
35 | int bus_info_created_ok; |
36 | int hdw_name_created_ok; |
37 | int hdw_desc_created_ok; |
38 | }; |
39 | |
40 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
41 | struct pvr2_sysfs_debugifc { |
42 | struct device_attribute attr_debugcmd; |
43 | struct device_attribute attr_debuginfo; |
44 | int debugcmd_created_ok; |
45 | int debuginfo_created_ok; |
46 | }; |
47 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
48 | |
49 | struct pvr2_sysfs_ctl_item { |
50 | struct device_attribute attr_name; |
51 | struct device_attribute attr_type; |
52 | struct device_attribute attr_min; |
53 | struct device_attribute attr_max; |
54 | struct device_attribute attr_def; |
55 | struct device_attribute attr_enum; |
56 | struct device_attribute attr_bits; |
57 | struct device_attribute attr_val; |
58 | struct device_attribute attr_custom; |
59 | struct pvr2_ctrl *cptr; |
60 | int ctl_id; |
61 | struct pvr2_sysfs *chptr; |
62 | struct pvr2_sysfs_ctl_item *item_next; |
63 | struct attribute *attr_gen[8]; |
64 | struct attribute_group grp; |
65 | int created_ok; |
66 | char name[80]; |
67 | }; |
68 | |
69 | static ssize_t show_name(struct device *class_dev, |
70 | struct device_attribute *attr, |
71 | char *buf) |
72 | { |
73 | struct pvr2_sysfs_ctl_item *cip; |
74 | const char *name; |
75 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); |
76 | name = pvr2_ctrl_get_desc(cip->cptr); |
77 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s" , |
78 | cip->chptr, cip->ctl_id, name); |
79 | if (!name) return -EINVAL; |
80 | return sysfs_emit(buf, fmt: "%s\n" , name); |
81 | } |
82 | |
83 | static ssize_t show_type(struct device *class_dev, |
84 | struct device_attribute *attr, |
85 | char *buf) |
86 | { |
87 | struct pvr2_sysfs_ctl_item *cip; |
88 | const char *name; |
89 | enum pvr2_ctl_type tp; |
90 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); |
91 | tp = pvr2_ctrl_get_type(cip->cptr); |
92 | switch (tp) { |
93 | case pvr2_ctl_int: name = "integer" ; break; |
94 | case pvr2_ctl_enum: name = "enum" ; break; |
95 | case pvr2_ctl_bitmask: name = "bitmask" ; break; |
96 | case pvr2_ctl_bool: name = "boolean" ; break; |
97 | default: name = "?" ; break; |
98 | } |
99 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s" , |
100 | cip->chptr, cip->ctl_id, name); |
101 | return sysfs_emit(buf, fmt: "%s\n" , name); |
102 | } |
103 | |
104 | static ssize_t show_min(struct device *class_dev, |
105 | struct device_attribute *attr, |
106 | char *buf) |
107 | { |
108 | struct pvr2_sysfs_ctl_item *cip; |
109 | long val; |
110 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); |
111 | val = pvr2_ctrl_get_min(cip->cptr); |
112 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld" , |
113 | cip->chptr, cip->ctl_id, val); |
114 | return sysfs_emit(buf, fmt: "%ld\n" , val); |
115 | } |
116 | |
117 | static ssize_t show_max(struct device *class_dev, |
118 | struct device_attribute *attr, |
119 | char *buf) |
120 | { |
121 | struct pvr2_sysfs_ctl_item *cip; |
122 | long val; |
123 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); |
124 | val = pvr2_ctrl_get_max(cip->cptr); |
125 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld" , |
126 | cip->chptr, cip->ctl_id, val); |
127 | return sysfs_emit(buf, fmt: "%ld\n" , val); |
128 | } |
129 | |
130 | static ssize_t show_def(struct device *class_dev, |
131 | struct device_attribute *attr, |
132 | char *buf) |
133 | { |
134 | struct pvr2_sysfs_ctl_item *cip; |
135 | int val; |
136 | int ret; |
137 | unsigned int cnt = 0; |
138 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); |
139 | ret = pvr2_ctrl_get_def(cip->cptr, valptr: &val); |
140 | if (ret < 0) return ret; |
141 | ret = pvr2_ctrl_value_to_sym(cip->cptr, mask: ~0, val, |
142 | buf, PAGE_SIZE - 1, len: &cnt); |
143 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)" , |
144 | cip->chptr, cip->ctl_id, cnt, buf, val); |
145 | buf[cnt] = '\n'; |
146 | return cnt + 1; |
147 | } |
148 | |
149 | static ssize_t show_val_norm(struct device *class_dev, |
150 | struct device_attribute *attr, |
151 | char *buf) |
152 | { |
153 | struct pvr2_sysfs_ctl_item *cip; |
154 | int val; |
155 | int ret; |
156 | unsigned int cnt = 0; |
157 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); |
158 | ret = pvr2_ctrl_get_value(cip->cptr, valptr: &val); |
159 | if (ret < 0) return ret; |
160 | ret = pvr2_ctrl_value_to_sym(cip->cptr, mask: ~0, val, |
161 | buf, PAGE_SIZE - 1, len: &cnt); |
162 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)" , |
163 | cip->chptr, cip->ctl_id, cnt, buf, val); |
164 | buf[cnt] = '\n'; |
165 | return cnt+1; |
166 | } |
167 | |
168 | static ssize_t show_val_custom(struct device *class_dev, |
169 | struct device_attribute *attr, |
170 | char *buf) |
171 | { |
172 | struct pvr2_sysfs_ctl_item *cip; |
173 | int val; |
174 | int ret; |
175 | unsigned int cnt = 0; |
176 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); |
177 | ret = pvr2_ctrl_get_value(cip->cptr, valptr: &val); |
178 | if (ret < 0) return ret; |
179 | ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, mask: ~0, val, |
180 | buf, PAGE_SIZE - 1, len: &cnt); |
181 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)" , |
182 | cip->chptr, cip->ctl_id, cnt, buf, val); |
183 | buf[cnt] = '\n'; |
184 | return cnt+1; |
185 | } |
186 | |
187 | static ssize_t show_enum(struct device *class_dev, |
188 | struct device_attribute *attr, |
189 | char *buf) |
190 | { |
191 | struct pvr2_sysfs_ctl_item *cip; |
192 | long val; |
193 | unsigned int bcnt, ccnt, ecnt; |
194 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); |
195 | ecnt = pvr2_ctrl_get_cnt(cip->cptr); |
196 | bcnt = 0; |
197 | for (val = 0; val < ecnt; val++) { |
198 | pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, |
199 | PAGE_SIZE - bcnt, &ccnt); |
200 | if (!ccnt) continue; |
201 | bcnt += ccnt; |
202 | if (bcnt >= PAGE_SIZE) break; |
203 | buf[bcnt] = '\n'; |
204 | bcnt++; |
205 | } |
206 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)" , |
207 | cip->chptr, cip->ctl_id); |
208 | return bcnt; |
209 | } |
210 | |
211 | static ssize_t show_bits(struct device *class_dev, |
212 | struct device_attribute *attr, |
213 | char *buf) |
214 | { |
215 | struct pvr2_sysfs_ctl_item *cip; |
216 | int valid_bits, msk; |
217 | unsigned int bcnt, ccnt; |
218 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); |
219 | valid_bits = pvr2_ctrl_get_mask(cip->cptr); |
220 | bcnt = 0; |
221 | for (msk = 1; valid_bits; msk <<= 1) { |
222 | if (!(msk & valid_bits)) continue; |
223 | valid_bits &= ~msk; |
224 | pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, |
225 | PAGE_SIZE - bcnt, &ccnt); |
226 | bcnt += ccnt; |
227 | if (bcnt >= PAGE_SIZE) break; |
228 | buf[bcnt] = '\n'; |
229 | bcnt++; |
230 | } |
231 | pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)" , |
232 | cip->chptr, cip->ctl_id); |
233 | return bcnt; |
234 | } |
235 | |
236 | static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, |
237 | const char *buf,unsigned int count) |
238 | { |
239 | int ret; |
240 | int mask,val; |
241 | if (customfl) { |
242 | ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, len: count, |
243 | maskptr: &mask, valptr: &val); |
244 | } else { |
245 | ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, len: count, |
246 | maskptr: &mask, valptr: &val); |
247 | } |
248 | if (ret < 0) return ret; |
249 | ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); |
250 | pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); |
251 | return ret; |
252 | } |
253 | |
254 | static ssize_t store_val_norm(struct device *class_dev, |
255 | struct device_attribute *attr, |
256 | const char *buf, size_t count) |
257 | { |
258 | struct pvr2_sysfs_ctl_item *cip; |
259 | int ret; |
260 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); |
261 | pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"" , |
262 | cip->chptr, cip->ctl_id, (int)count, buf); |
263 | ret = store_val_any(cip, customfl: 0, buf, count); |
264 | if (!ret) ret = count; |
265 | return ret; |
266 | } |
267 | |
268 | static ssize_t store_val_custom(struct device *class_dev, |
269 | struct device_attribute *attr, |
270 | const char *buf, size_t count) |
271 | { |
272 | struct pvr2_sysfs_ctl_item *cip; |
273 | int ret; |
274 | cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); |
275 | pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"" , |
276 | cip->chptr, cip->ctl_id, (int)count, buf); |
277 | ret = store_val_any(cip, customfl: 1, buf, count); |
278 | if (!ret) ret = count; |
279 | return ret; |
280 | } |
281 | |
282 | static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) |
283 | { |
284 | struct pvr2_sysfs_ctl_item *cip; |
285 | struct pvr2_ctrl *cptr; |
286 | unsigned int cnt,acnt; |
287 | int ret; |
288 | |
289 | cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); |
290 | if (!cptr) return; |
291 | |
292 | cip = kzalloc(size: sizeof(*cip),GFP_KERNEL); |
293 | if (!cip) return; |
294 | pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p" ,cip); |
295 | |
296 | cip->cptr = cptr; |
297 | cip->ctl_id = ctl_id; |
298 | |
299 | cip->chptr = sfp; |
300 | cip->item_next = NULL; |
301 | if (sfp->item_last) { |
302 | sfp->item_last->item_next = cip; |
303 | } else { |
304 | sfp->item_first = cip; |
305 | } |
306 | sfp->item_last = cip; |
307 | |
308 | sysfs_attr_init(&cip->attr_name.attr); |
309 | cip->attr_name.attr.name = "name" ; |
310 | cip->attr_name.attr.mode = S_IRUGO; |
311 | cip->attr_name.show = show_name; |
312 | |
313 | sysfs_attr_init(&cip->attr_type.attr); |
314 | cip->attr_type.attr.name = "type" ; |
315 | cip->attr_type.attr.mode = S_IRUGO; |
316 | cip->attr_type.show = show_type; |
317 | |
318 | sysfs_attr_init(&cip->attr_min.attr); |
319 | cip->attr_min.attr.name = "min_val" ; |
320 | cip->attr_min.attr.mode = S_IRUGO; |
321 | cip->attr_min.show = show_min; |
322 | |
323 | sysfs_attr_init(&cip->attr_max.attr); |
324 | cip->attr_max.attr.name = "max_val" ; |
325 | cip->attr_max.attr.mode = S_IRUGO; |
326 | cip->attr_max.show = show_max; |
327 | |
328 | sysfs_attr_init(&cip->attr_def.attr); |
329 | cip->attr_def.attr.name = "def_val" ; |
330 | cip->attr_def.attr.mode = S_IRUGO; |
331 | cip->attr_def.show = show_def; |
332 | |
333 | sysfs_attr_init(&cip->attr_val.attr); |
334 | cip->attr_val.attr.name = "cur_val" ; |
335 | cip->attr_val.attr.mode = S_IRUGO; |
336 | |
337 | sysfs_attr_init(&cip->attr_custom.attr); |
338 | cip->attr_custom.attr.name = "custom_val" ; |
339 | cip->attr_custom.attr.mode = S_IRUGO; |
340 | |
341 | sysfs_attr_init(&cip->attr_enum.attr); |
342 | cip->attr_enum.attr.name = "enum_val" ; |
343 | cip->attr_enum.attr.mode = S_IRUGO; |
344 | cip->attr_enum.show = show_enum; |
345 | |
346 | sysfs_attr_init(&cip->attr_bits.attr); |
347 | cip->attr_bits.attr.name = "bit_val" ; |
348 | cip->attr_bits.attr.mode = S_IRUGO; |
349 | cip->attr_bits.show = show_bits; |
350 | |
351 | if (pvr2_ctrl_is_writable(cptr)) { |
352 | cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; |
353 | cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; |
354 | } |
355 | |
356 | acnt = 0; |
357 | cip->attr_gen[acnt++] = &cip->attr_name.attr; |
358 | cip->attr_gen[acnt++] = &cip->attr_type.attr; |
359 | cip->attr_gen[acnt++] = &cip->attr_val.attr; |
360 | cip->attr_gen[acnt++] = &cip->attr_def.attr; |
361 | cip->attr_val.show = show_val_norm; |
362 | cip->attr_val.store = store_val_norm; |
363 | if (pvr2_ctrl_has_custom_symbols(cptr)) { |
364 | cip->attr_gen[acnt++] = &cip->attr_custom.attr; |
365 | cip->attr_custom.show = show_val_custom; |
366 | cip->attr_custom.store = store_val_custom; |
367 | } |
368 | switch (pvr2_ctrl_get_type(cptr)) { |
369 | case pvr2_ctl_enum: |
370 | // Control is an enumeration |
371 | cip->attr_gen[acnt++] = &cip->attr_enum.attr; |
372 | break; |
373 | case pvr2_ctl_int: |
374 | // Control is an integer |
375 | cip->attr_gen[acnt++] = &cip->attr_min.attr; |
376 | cip->attr_gen[acnt++] = &cip->attr_max.attr; |
377 | break; |
378 | case pvr2_ctl_bitmask: |
379 | // Control is an bitmask |
380 | cip->attr_gen[acnt++] = &cip->attr_bits.attr; |
381 | break; |
382 | default: break; |
383 | } |
384 | |
385 | cnt = scnprintf(buf: cip->name,size: sizeof(cip->name)-1,fmt: "ctl_%s" , |
386 | pvr2_ctrl_get_name(cptr)); |
387 | cip->name[cnt] = 0; |
388 | cip->grp.name = cip->name; |
389 | cip->grp.attrs = cip->attr_gen; |
390 | |
391 | ret = sysfs_create_group(kobj: &sfp->class_dev->kobj,grp: &cip->grp); |
392 | if (ret) { |
393 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
394 | "sysfs_create_group error: %d" , |
395 | ret); |
396 | return; |
397 | } |
398 | cip->created_ok = !0; |
399 | } |
400 | |
401 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
402 | static ssize_t debuginfo_show(struct device *, struct device_attribute *, |
403 | char *); |
404 | static ssize_t debugcmd_show(struct device *, struct device_attribute *, |
405 | char *); |
406 | static ssize_t debugcmd_store(struct device *, struct device_attribute *, |
407 | const char *, size_t count); |
408 | |
409 | static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) |
410 | { |
411 | struct pvr2_sysfs_debugifc *dip; |
412 | int ret; |
413 | |
414 | dip = kzalloc(size: sizeof(*dip),GFP_KERNEL); |
415 | if (!dip) return; |
416 | sysfs_attr_init(&dip->attr_debugcmd.attr); |
417 | dip->attr_debugcmd.attr.name = "debugcmd" ; |
418 | dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; |
419 | dip->attr_debugcmd.show = debugcmd_show; |
420 | dip->attr_debugcmd.store = debugcmd_store; |
421 | sysfs_attr_init(&dip->attr_debuginfo.attr); |
422 | dip->attr_debuginfo.attr.name = "debuginfo" ; |
423 | dip->attr_debuginfo.attr.mode = S_IRUGO; |
424 | dip->attr_debuginfo.show = debuginfo_show; |
425 | sfp->debugifc = dip; |
426 | ret = device_create_file(device: sfp->class_dev,entry: &dip->attr_debugcmd); |
427 | if (ret < 0) { |
428 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
429 | "device_create_file error: %d" , |
430 | ret); |
431 | } else { |
432 | dip->debugcmd_created_ok = !0; |
433 | } |
434 | ret = device_create_file(device: sfp->class_dev,entry: &dip->attr_debuginfo); |
435 | if (ret < 0) { |
436 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
437 | "device_create_file error: %d" , |
438 | ret); |
439 | } else { |
440 | dip->debuginfo_created_ok = !0; |
441 | } |
442 | } |
443 | |
444 | |
445 | static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) |
446 | { |
447 | if (!sfp->debugifc) return; |
448 | if (sfp->debugifc->debuginfo_created_ok) { |
449 | device_remove_file(dev: sfp->class_dev, |
450 | attr: &sfp->debugifc->attr_debuginfo); |
451 | } |
452 | if (sfp->debugifc->debugcmd_created_ok) { |
453 | device_remove_file(dev: sfp->class_dev, |
454 | attr: &sfp->debugifc->attr_debugcmd); |
455 | } |
456 | kfree(objp: sfp->debugifc); |
457 | sfp->debugifc = NULL; |
458 | } |
459 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
460 | |
461 | |
462 | static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) |
463 | { |
464 | unsigned int idx,cnt; |
465 | cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); |
466 | for (idx = 0; idx < cnt; idx++) { |
467 | pvr2_sysfs_add_control(sfp,ctl_id: idx); |
468 | } |
469 | } |
470 | |
471 | |
472 | static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) |
473 | { |
474 | struct pvr2_sysfs_ctl_item *cip1,*cip2; |
475 | for (cip1 = sfp->item_first; cip1; cip1 = cip2) { |
476 | cip2 = cip1->item_next; |
477 | if (cip1->created_ok) { |
478 | sysfs_remove_group(kobj: &sfp->class_dev->kobj,grp: &cip1->grp); |
479 | } |
480 | pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p" ,cip1); |
481 | kfree(objp: cip1); |
482 | } |
483 | } |
484 | |
485 | |
486 | static void pvr2_sysfs_release(struct device *class_dev) |
487 | { |
488 | pvr2_sysfs_trace("Releasing class_dev id=%p" ,class_dev); |
489 | kfree(objp: class_dev); |
490 | } |
491 | |
492 | |
493 | static struct class pvr2_class = { |
494 | .name = "pvrusb2" , |
495 | .dev_release = pvr2_sysfs_release, |
496 | }; |
497 | |
498 | |
499 | static void class_dev_destroy(struct pvr2_sysfs *sfp) |
500 | { |
501 | struct device *dev; |
502 | if (!sfp->class_dev) return; |
503 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
504 | pvr2_sysfs_tear_down_debugifc(sfp); |
505 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
506 | pvr2_sysfs_tear_down_controls(sfp); |
507 | if (sfp->hdw_desc_created_ok) { |
508 | device_remove_file(dev: sfp->class_dev, |
509 | attr: &sfp->attr_hdw_desc); |
510 | } |
511 | if (sfp->hdw_name_created_ok) { |
512 | device_remove_file(dev: sfp->class_dev, |
513 | attr: &sfp->attr_hdw_name); |
514 | } |
515 | if (sfp->bus_info_created_ok) { |
516 | device_remove_file(dev: sfp->class_dev, |
517 | attr: &sfp->attr_bus_info); |
518 | } |
519 | if (sfp->v4l_minor_number_created_ok) { |
520 | device_remove_file(dev: sfp->class_dev, |
521 | attr: &sfp->attr_v4l_minor_number); |
522 | } |
523 | if (sfp->v4l_radio_minor_number_created_ok) { |
524 | device_remove_file(dev: sfp->class_dev, |
525 | attr: &sfp->attr_v4l_radio_minor_number); |
526 | } |
527 | if (sfp->unit_number_created_ok) { |
528 | device_remove_file(dev: sfp->class_dev, |
529 | attr: &sfp->attr_unit_number); |
530 | } |
531 | pvr2_sysfs_trace("Destroying class_dev id=%p" ,sfp->class_dev); |
532 | dev_set_drvdata(dev: sfp->class_dev, NULL); |
533 | dev = sfp->class_dev->parent; |
534 | sfp->class_dev->parent = NULL; |
535 | put_device(dev); |
536 | device_unregister(dev: sfp->class_dev); |
537 | sfp->class_dev = NULL; |
538 | } |
539 | |
540 | |
541 | static ssize_t v4l_minor_number_show(struct device *class_dev, |
542 | struct device_attribute *attr, char *buf) |
543 | { |
544 | struct pvr2_sysfs *sfp; |
545 | sfp = dev_get_drvdata(dev: class_dev); |
546 | if (!sfp) return -EINVAL; |
547 | return sysfs_emit(buf, fmt: "%d\n" , |
548 | pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, |
549 | index: pvr2_v4l_type_video)); |
550 | } |
551 | |
552 | |
553 | static ssize_t bus_info_show(struct device *class_dev, |
554 | struct device_attribute *attr, char *buf) |
555 | { |
556 | struct pvr2_sysfs *sfp; |
557 | sfp = dev_get_drvdata(dev: class_dev); |
558 | if (!sfp) return -EINVAL; |
559 | return sysfs_emit(buf, fmt: "%s\n" , |
560 | pvr2_hdw_get_bus_info(sfp->channel.hdw)); |
561 | } |
562 | |
563 | |
564 | static ssize_t hdw_name_show(struct device *class_dev, |
565 | struct device_attribute *attr, char *buf) |
566 | { |
567 | struct pvr2_sysfs *sfp; |
568 | sfp = dev_get_drvdata(dev: class_dev); |
569 | if (!sfp) return -EINVAL; |
570 | return sysfs_emit(buf, fmt: "%s\n" , |
571 | pvr2_hdw_get_type(sfp->channel.hdw)); |
572 | } |
573 | |
574 | |
575 | static ssize_t hdw_desc_show(struct device *class_dev, |
576 | struct device_attribute *attr, char *buf) |
577 | { |
578 | struct pvr2_sysfs *sfp; |
579 | sfp = dev_get_drvdata(dev: class_dev); |
580 | if (!sfp) return -EINVAL; |
581 | return sysfs_emit(buf, fmt: "%s\n" , |
582 | pvr2_hdw_get_desc(sfp->channel.hdw)); |
583 | } |
584 | |
585 | |
586 | static ssize_t v4l_radio_minor_number_show(struct device *class_dev, |
587 | struct device_attribute *attr, |
588 | char *buf) |
589 | { |
590 | struct pvr2_sysfs *sfp; |
591 | sfp = dev_get_drvdata(dev: class_dev); |
592 | if (!sfp) return -EINVAL; |
593 | return sysfs_emit(buf, fmt: "%d\n" , |
594 | pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, |
595 | index: pvr2_v4l_type_radio)); |
596 | } |
597 | |
598 | |
599 | static ssize_t unit_number_show(struct device *class_dev, |
600 | struct device_attribute *attr, char *buf) |
601 | { |
602 | struct pvr2_sysfs *sfp; |
603 | sfp = dev_get_drvdata(dev: class_dev); |
604 | if (!sfp) return -EINVAL; |
605 | return sysfs_emit(buf, fmt: "%d\n" , |
606 | pvr2_hdw_get_unit_number(sfp->channel.hdw)); |
607 | } |
608 | |
609 | |
610 | static void class_dev_create(struct pvr2_sysfs *sfp) |
611 | { |
612 | struct usb_device *usb_dev; |
613 | struct device *class_dev; |
614 | int ret; |
615 | |
616 | usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); |
617 | if (!usb_dev) return; |
618 | class_dev = kzalloc(size: sizeof(*class_dev),GFP_KERNEL); |
619 | if (!class_dev) return; |
620 | |
621 | pvr2_sysfs_trace("Creating class_dev id=%p" ,class_dev); |
622 | |
623 | class_dev->class = &pvr2_class; |
624 | |
625 | dev_set_name(dev: class_dev, name: "%s" , |
626 | pvr2_hdw_get_device_identifier(sfp->channel.hdw)); |
627 | |
628 | class_dev->parent = get_device(dev: &usb_dev->dev); |
629 | |
630 | sfp->class_dev = class_dev; |
631 | dev_set_drvdata(dev: class_dev, data: sfp); |
632 | ret = device_register(dev: class_dev); |
633 | if (ret) { |
634 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
635 | "device_register failed" ); |
636 | put_device(dev: class_dev); |
637 | return; |
638 | } |
639 | |
640 | sysfs_attr_init(&sfp->attr_v4l_minor_number.attr); |
641 | sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number" ; |
642 | sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; |
643 | sfp->attr_v4l_minor_number.show = v4l_minor_number_show; |
644 | sfp->attr_v4l_minor_number.store = NULL; |
645 | ret = device_create_file(device: sfp->class_dev, |
646 | entry: &sfp->attr_v4l_minor_number); |
647 | if (ret < 0) { |
648 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
649 | "device_create_file error: %d" , |
650 | ret); |
651 | } else { |
652 | sfp->v4l_minor_number_created_ok = !0; |
653 | } |
654 | |
655 | sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr); |
656 | sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number" ; |
657 | sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; |
658 | sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; |
659 | sfp->attr_v4l_radio_minor_number.store = NULL; |
660 | ret = device_create_file(device: sfp->class_dev, |
661 | entry: &sfp->attr_v4l_radio_minor_number); |
662 | if (ret < 0) { |
663 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
664 | "device_create_file error: %d" , |
665 | ret); |
666 | } else { |
667 | sfp->v4l_radio_minor_number_created_ok = !0; |
668 | } |
669 | |
670 | sysfs_attr_init(&sfp->attr_unit_number.attr); |
671 | sfp->attr_unit_number.attr.name = "unit_number" ; |
672 | sfp->attr_unit_number.attr.mode = S_IRUGO; |
673 | sfp->attr_unit_number.show = unit_number_show; |
674 | sfp->attr_unit_number.store = NULL; |
675 | ret = device_create_file(device: sfp->class_dev,entry: &sfp->attr_unit_number); |
676 | if (ret < 0) { |
677 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
678 | "device_create_file error: %d" , |
679 | ret); |
680 | } else { |
681 | sfp->unit_number_created_ok = !0; |
682 | } |
683 | |
684 | sysfs_attr_init(&sfp->attr_bus_info.attr); |
685 | sfp->attr_bus_info.attr.name = "bus_info_str" ; |
686 | sfp->attr_bus_info.attr.mode = S_IRUGO; |
687 | sfp->attr_bus_info.show = bus_info_show; |
688 | sfp->attr_bus_info.store = NULL; |
689 | ret = device_create_file(device: sfp->class_dev, |
690 | entry: &sfp->attr_bus_info); |
691 | if (ret < 0) { |
692 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
693 | "device_create_file error: %d" , |
694 | ret); |
695 | } else { |
696 | sfp->bus_info_created_ok = !0; |
697 | } |
698 | |
699 | sysfs_attr_init(&sfp->attr_hdw_name.attr); |
700 | sfp->attr_hdw_name.attr.name = "device_hardware_type" ; |
701 | sfp->attr_hdw_name.attr.mode = S_IRUGO; |
702 | sfp->attr_hdw_name.show = hdw_name_show; |
703 | sfp->attr_hdw_name.store = NULL; |
704 | ret = device_create_file(device: sfp->class_dev, |
705 | entry: &sfp->attr_hdw_name); |
706 | if (ret < 0) { |
707 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
708 | "device_create_file error: %d" , |
709 | ret); |
710 | } else { |
711 | sfp->hdw_name_created_ok = !0; |
712 | } |
713 | |
714 | sysfs_attr_init(&sfp->attr_hdw_desc.attr); |
715 | sfp->attr_hdw_desc.attr.name = "device_hardware_description" ; |
716 | sfp->attr_hdw_desc.attr.mode = S_IRUGO; |
717 | sfp->attr_hdw_desc.show = hdw_desc_show; |
718 | sfp->attr_hdw_desc.store = NULL; |
719 | ret = device_create_file(device: sfp->class_dev, |
720 | entry: &sfp->attr_hdw_desc); |
721 | if (ret < 0) { |
722 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
723 | "device_create_file error: %d" , |
724 | ret); |
725 | } else { |
726 | sfp->hdw_desc_created_ok = !0; |
727 | } |
728 | |
729 | pvr2_sysfs_add_controls(sfp); |
730 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
731 | pvr2_sysfs_add_debugifc(sfp); |
732 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
733 | } |
734 | |
735 | |
736 | static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) |
737 | { |
738 | struct pvr2_sysfs *sfp; |
739 | sfp = container_of(chp,struct pvr2_sysfs,channel); |
740 | if (!sfp->channel.mc_head->disconnect_flag) return; |
741 | pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p" ,sfp); |
742 | class_dev_destroy(sfp); |
743 | pvr2_channel_done(&sfp->channel); |
744 | kfree(objp: sfp); |
745 | } |
746 | |
747 | |
748 | void pvr2_sysfs_create(struct pvr2_context *mp) |
749 | { |
750 | struct pvr2_sysfs *sfp; |
751 | sfp = kzalloc(size: sizeof(*sfp),GFP_KERNEL); |
752 | if (!sfp) |
753 | return; |
754 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p" ,sfp); |
755 | pvr2_channel_init(&sfp->channel,mp); |
756 | sfp->channel.check_func = pvr2_sysfs_internal_check; |
757 | |
758 | class_dev_create(sfp); |
759 | } |
760 | |
761 | |
762 | void pvr2_sysfs_class_create(void) |
763 | { |
764 | if (class_register(class: &pvr2_class)) |
765 | pvr2_sysfs_trace("Registration failed for pvr2_sysfs_class" ); |
766 | } |
767 | |
768 | |
769 | void pvr2_sysfs_class_destroy(void) |
770 | { |
771 | class_unregister(class: &pvr2_class); |
772 | } |
773 | |
774 | |
775 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC |
776 | static ssize_t debuginfo_show(struct device *class_dev, |
777 | struct device_attribute *attr, char *buf) |
778 | { |
779 | struct pvr2_sysfs *sfp; |
780 | sfp = dev_get_drvdata(dev: class_dev); |
781 | if (!sfp) return -EINVAL; |
782 | pvr2_hdw_trigger_module_log(hdw: sfp->channel.hdw); |
783 | return pvr2_debugifc_print_info(sfp->channel.hdw,buf_ptr: buf,PAGE_SIZE); |
784 | } |
785 | |
786 | |
787 | static ssize_t debugcmd_show(struct device *class_dev, |
788 | struct device_attribute *attr, char *buf) |
789 | { |
790 | struct pvr2_sysfs *sfp; |
791 | sfp = dev_get_drvdata(dev: class_dev); |
792 | if (!sfp) return -EINVAL; |
793 | return pvr2_debugifc_print_status(sfp->channel.hdw,buf_ptr: buf,PAGE_SIZE); |
794 | } |
795 | |
796 | |
797 | static ssize_t debugcmd_store(struct device *class_dev, |
798 | struct device_attribute *attr, |
799 | const char *buf, size_t count) |
800 | { |
801 | struct pvr2_sysfs *sfp; |
802 | int ret; |
803 | |
804 | sfp = dev_get_drvdata(dev: class_dev); |
805 | if (!sfp) return -EINVAL; |
806 | |
807 | ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf_ptr: buf,buf_size: count); |
808 | if (ret < 0) return ret; |
809 | return count; |
810 | } |
811 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ |
812 | |