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