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