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