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