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