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 | ||
19a0a296 | 327 | sysfs_attr_init(&cip->attr_name.attr); |
d855497e MI |
328 | cip->attr_name.attr.name = "name"; |
329 | cip->attr_name.attr.mode = S_IRUGO; | |
f90fe7a3 | 330 | cip->attr_name.show = show_name; |
d855497e | 331 | |
19a0a296 | 332 | sysfs_attr_init(&cip->attr_type.attr); |
33213963 MI |
333 | cip->attr_type.attr.name = "type"; |
334 | cip->attr_type.attr.mode = S_IRUGO; | |
f90fe7a3 | 335 | cip->attr_type.show = show_type; |
33213963 | 336 | |
19a0a296 | 337 | sysfs_attr_init(&cip->attr_min.attr); |
d855497e MI |
338 | cip->attr_min.attr.name = "min_val"; |
339 | cip->attr_min.attr.mode = S_IRUGO; | |
f90fe7a3 | 340 | cip->attr_min.show = show_min; |
d855497e | 341 | |
19a0a296 | 342 | sysfs_attr_init(&cip->attr_max.attr); |
d855497e MI |
343 | cip->attr_max.attr.name = "max_val"; |
344 | cip->attr_max.attr.mode = S_IRUGO; | |
f90fe7a3 | 345 | cip->attr_max.show = show_max; |
d855497e | 346 | |
19a0a296 | 347 | sysfs_attr_init(&cip->attr_def.attr); |
0b7c2c95 MI |
348 | cip->attr_def.attr.name = "def_val"; |
349 | cip->attr_def.attr.mode = S_IRUGO; | |
350 | cip->attr_def.show = show_def; | |
351 | ||
19a0a296 | 352 | sysfs_attr_init(&cip->attr_val.attr); |
d855497e MI |
353 | cip->attr_val.attr.name = "cur_val"; |
354 | cip->attr_val.attr.mode = S_IRUGO; | |
355 | ||
19a0a296 | 356 | sysfs_attr_init(&cip->attr_custom.attr); |
d855497e MI |
357 | cip->attr_custom.attr.name = "custom_val"; |
358 | cip->attr_custom.attr.mode = S_IRUGO; | |
359 | ||
19a0a296 | 360 | sysfs_attr_init(&cip->attr_enum.attr); |
d855497e MI |
361 | cip->attr_enum.attr.name = "enum_val"; |
362 | cip->attr_enum.attr.mode = S_IRUGO; | |
f90fe7a3 | 363 | cip->attr_enum.show = show_enum; |
d855497e | 364 | |
19a0a296 | 365 | sysfs_attr_init(&cip->attr_bits.attr); |
d855497e MI |
366 | cip->attr_bits.attr.name = "bit_val"; |
367 | cip->attr_bits.attr.mode = S_IRUGO; | |
f90fe7a3 | 368 | cip->attr_bits.show = show_bits; |
d855497e MI |
369 | |
370 | if (pvr2_ctrl_is_writable(cptr)) { | |
371 | cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; | |
372 | cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; | |
373 | } | |
374 | ||
375 | acnt = 0; | |
376 | cip->attr_gen[acnt++] = &cip->attr_name.attr; | |
33213963 | 377 | cip->attr_gen[acnt++] = &cip->attr_type.attr; |
d855497e | 378 | cip->attr_gen[acnt++] = &cip->attr_val.attr; |
0b7c2c95 | 379 | cip->attr_gen[acnt++] = &cip->attr_def.attr; |
f90fe7a3 MI |
380 | cip->attr_val.show = show_val_norm; |
381 | cip->attr_val.store = store_val_norm; | |
d855497e MI |
382 | if (pvr2_ctrl_has_custom_symbols(cptr)) { |
383 | cip->attr_gen[acnt++] = &cip->attr_custom.attr; | |
f90fe7a3 MI |
384 | cip->attr_custom.show = show_val_custom; |
385 | cip->attr_custom.store = store_val_custom; | |
d855497e MI |
386 | } |
387 | switch (pvr2_ctrl_get_type(cptr)) { | |
388 | case pvr2_ctl_enum: | |
389 | // Control is an enumeration | |
390 | cip->attr_gen[acnt++] = &cip->attr_enum.attr; | |
391 | break; | |
392 | case pvr2_ctl_int: | |
393 | // Control is an integer | |
394 | cip->attr_gen[acnt++] = &cip->attr_min.attr; | |
395 | cip->attr_gen[acnt++] = &cip->attr_max.attr; | |
396 | break; | |
397 | case pvr2_ctl_bitmask: | |
398 | // Control is an bitmask | |
399 | cip->attr_gen[acnt++] = &cip->attr_bits.attr; | |
400 | break; | |
401 | default: break; | |
402 | } | |
403 | ||
404 | cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", | |
405 | pvr2_ctrl_get_name(cptr)); | |
406 | cip->name[cnt] = 0; | |
407 | cip->grp.name = cip->name; | |
408 | cip->grp.attrs = cip->attr_gen; | |
409 | ||
08d41808 MI |
410 | ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); |
411 | if (ret) { | |
49844c29 MI |
412 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
413 | "sysfs_create_group error: %d", | |
414 | ret); | |
08d41808 MI |
415 | return; |
416 | } | |
417 | cip->created_ok = !0; | |
d855497e MI |
418 | } |
419 | ||
420 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | |
c726b65d TP |
421 | static ssize_t debuginfo_show(struct device *, struct device_attribute *, |
422 | char *); | |
423 | static ssize_t debugcmd_show(struct device *, struct device_attribute *, | |
424 | char *); | |
425 | static ssize_t debugcmd_store(struct device *, struct device_attribute *, | |
426 | const char *, size_t count); | |
d855497e MI |
427 | |
428 | static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) | |
429 | { | |
430 | struct pvr2_sysfs_debugifc *dip; | |
3117beec MK |
431 | int ret; |
432 | ||
ca545f7c | 433 | dip = kzalloc(sizeof(*dip),GFP_KERNEL); |
d855497e | 434 | if (!dip) return; |
12765517 | 435 | sysfs_attr_init(&dip->attr_debugcmd.attr); |
d855497e MI |
436 | dip->attr_debugcmd.attr.name = "debugcmd"; |
437 | dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; | |
438 | dip->attr_debugcmd.show = debugcmd_show; | |
439 | dip->attr_debugcmd.store = debugcmd_store; | |
12765517 | 440 | sysfs_attr_init(&dip->attr_debuginfo.attr); |
d855497e MI |
441 | dip->attr_debuginfo.attr.name = "debuginfo"; |
442 | dip->attr_debuginfo.attr.mode = S_IRUGO; | |
443 | dip->attr_debuginfo.show = debuginfo_show; | |
444 | sfp->debugifc = dip; | |
54bd5b66 | 445 | ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); |
08d41808 | 446 | if (ret < 0) { |
49844c29 MI |
447 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
448 | "device_create_file error: %d", | |
449 | ret); | |
08d41808 MI |
450 | } else { |
451 | dip->debugcmd_created_ok = !0; | |
452 | } | |
54bd5b66 | 453 | ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); |
08d41808 | 454 | if (ret < 0) { |
49844c29 MI |
455 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
456 | "device_create_file error: %d", | |
457 | ret); | |
08d41808 MI |
458 | } else { |
459 | dip->debuginfo_created_ok = !0; | |
460 | } | |
d855497e MI |
461 | } |
462 | ||
463 | ||
464 | static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) | |
465 | { | |
466 | if (!sfp->debugifc) return; | |
08d41808 | 467 | if (sfp->debugifc->debuginfo_created_ok) { |
54bd5b66 | 468 | device_remove_file(sfp->class_dev, |
08d41808 MI |
469 | &sfp->debugifc->attr_debuginfo); |
470 | } | |
471 | if (sfp->debugifc->debugcmd_created_ok) { | |
54bd5b66 | 472 | device_remove_file(sfp->class_dev, |
08d41808 MI |
473 | &sfp->debugifc->attr_debugcmd); |
474 | } | |
d855497e | 475 | kfree(sfp->debugifc); |
a0fd1cb1 | 476 | sfp->debugifc = NULL; |
d855497e MI |
477 | } |
478 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | |
479 | ||
480 | ||
481 | static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) | |
482 | { | |
483 | unsigned int idx,cnt; | |
484 | cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); | |
485 | for (idx = 0; idx < cnt; idx++) { | |
486 | pvr2_sysfs_add_control(sfp,idx); | |
487 | } | |
488 | } | |
489 | ||
490 | ||
491 | static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp) | |
492 | { | |
493 | struct pvr2_sysfs_ctl_item *cip1,*cip2; | |
494 | for (cip1 = sfp->item_first; cip1; cip1 = cip2) { | |
495 | cip2 = cip1->item_next; | |
08d41808 MI |
496 | if (cip1->created_ok) { |
497 | sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp); | |
498 | } | |
d855497e MI |
499 | pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1); |
500 | kfree(cip1); | |
501 | } | |
502 | } | |
503 | ||
504 | ||
505 | static void pvr2_sysfs_class_release(struct class *class) | |
506 | { | |
507 | struct pvr2_sysfs_class *clp; | |
508 | clp = container_of(class,struct pvr2_sysfs_class,class); | |
509 | pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp); | |
510 | kfree(clp); | |
511 | } | |
512 | ||
513 | ||
54bd5b66 | 514 | static void pvr2_sysfs_release(struct device *class_dev) |
d855497e MI |
515 | { |
516 | pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev); | |
517 | kfree(class_dev); | |
518 | } | |
519 | ||
520 | ||
521 | static void class_dev_destroy(struct pvr2_sysfs *sfp) | |
522 | { | |
28c4a5e6 | 523 | struct device *dev; |
d855497e MI |
524 | if (!sfp->class_dev) return; |
525 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | |
526 | pvr2_sysfs_tear_down_debugifc(sfp); | |
527 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | |
528 | pvr2_sysfs_tear_down_controls(sfp); | |
78a47101 MI |
529 | if (sfp->hdw_desc_created_ok) { |
530 | device_remove_file(sfp->class_dev, | |
531 | &sfp->attr_hdw_desc); | |
532 | } | |
533 | if (sfp->hdw_name_created_ok) { | |
534 | device_remove_file(sfp->class_dev, | |
535 | &sfp->attr_hdw_name); | |
536 | } | |
31a18547 | 537 | if (sfp->bus_info_created_ok) { |
54bd5b66 | 538 | device_remove_file(sfp->class_dev, |
31a18547 MI |
539 | &sfp->attr_bus_info); |
540 | } | |
08d41808 | 541 | if (sfp->v4l_minor_number_created_ok) { |
54bd5b66 | 542 | device_remove_file(sfp->class_dev, |
08d41808 MI |
543 | &sfp->attr_v4l_minor_number); |
544 | } | |
2fdf3d9c | 545 | if (sfp->v4l_radio_minor_number_created_ok) { |
54bd5b66 | 546 | device_remove_file(sfp->class_dev, |
2fdf3d9c PK |
547 | &sfp->attr_v4l_radio_minor_number); |
548 | } | |
08d41808 | 549 | if (sfp->unit_number_created_ok) { |
54bd5b66 | 550 | device_remove_file(sfp->class_dev, |
08d41808 MI |
551 | &sfp->attr_unit_number); |
552 | } | |
d855497e | 553 | pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); |
79510cdb | 554 | dev_set_drvdata(sfp->class_dev, NULL); |
28c4a5e6 MI |
555 | dev = sfp->class_dev->parent; |
556 | sfp->class_dev->parent = NULL; | |
557 | put_device(dev); | |
54bd5b66 | 558 | device_unregister(sfp->class_dev); |
a0fd1cb1 | 559 | sfp->class_dev = NULL; |
d855497e MI |
560 | } |
561 | ||
562 | ||
54bd5b66 KS |
563 | static ssize_t v4l_minor_number_show(struct device *class_dev, |
564 | struct device_attribute *attr, char *buf) | |
d855497e MI |
565 | { |
566 | struct pvr2_sysfs *sfp; | |
79510cdb | 567 | sfp = dev_get_drvdata(class_dev); |
d855497e MI |
568 | if (!sfp) return -EINVAL; |
569 | return scnprintf(buf,PAGE_SIZE,"%d\n", | |
fd5a75fe | 570 | pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, |
8079384e | 571 | pvr2_v4l_type_video)); |
2fdf3d9c PK |
572 | } |
573 | ||
574 | ||
54bd5b66 KS |
575 | static ssize_t bus_info_show(struct device *class_dev, |
576 | struct device_attribute *attr, char *buf) | |
31a18547 MI |
577 | { |
578 | struct pvr2_sysfs *sfp; | |
79510cdb | 579 | sfp = dev_get_drvdata(class_dev); |
31a18547 MI |
580 | if (!sfp) return -EINVAL; |
581 | return scnprintf(buf,PAGE_SIZE,"%s\n", | |
582 | pvr2_hdw_get_bus_info(sfp->channel.hdw)); | |
583 | } | |
584 | ||
585 | ||
78a47101 MI |
586 | static ssize_t hdw_name_show(struct device *class_dev, |
587 | struct device_attribute *attr, char *buf) | |
588 | { | |
589 | struct pvr2_sysfs *sfp; | |
79510cdb | 590 | sfp = dev_get_drvdata(class_dev); |
78a47101 MI |
591 | if (!sfp) return -EINVAL; |
592 | return scnprintf(buf,PAGE_SIZE,"%s\n", | |
593 | pvr2_hdw_get_type(sfp->channel.hdw)); | |
594 | } | |
595 | ||
596 | ||
597 | static ssize_t hdw_desc_show(struct device *class_dev, | |
598 | struct device_attribute *attr, char *buf) | |
599 | { | |
600 | struct pvr2_sysfs *sfp; | |
79510cdb | 601 | sfp = dev_get_drvdata(class_dev); |
78a47101 MI |
602 | if (!sfp) return -EINVAL; |
603 | return scnprintf(buf,PAGE_SIZE,"%s\n", | |
604 | pvr2_hdw_get_desc(sfp->channel.hdw)); | |
605 | } | |
606 | ||
607 | ||
54bd5b66 KS |
608 | static ssize_t v4l_radio_minor_number_show(struct device *class_dev, |
609 | struct device_attribute *attr, | |
2fdf3d9c PK |
610 | char *buf) |
611 | { | |
612 | struct pvr2_sysfs *sfp; | |
79510cdb | 613 | sfp = dev_get_drvdata(class_dev); |
2fdf3d9c PK |
614 | if (!sfp) return -EINVAL; |
615 | return scnprintf(buf,PAGE_SIZE,"%d\n", | |
fd5a75fe | 616 | pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, |
8079384e | 617 | pvr2_v4l_type_radio)); |
d855497e MI |
618 | } |
619 | ||
620 | ||
54bd5b66 KS |
621 | static ssize_t unit_number_show(struct device *class_dev, |
622 | struct device_attribute *attr, char *buf) | |
d855497e MI |
623 | { |
624 | struct pvr2_sysfs *sfp; | |
79510cdb | 625 | sfp = dev_get_drvdata(class_dev); |
d855497e MI |
626 | if (!sfp) return -EINVAL; |
627 | return scnprintf(buf,PAGE_SIZE,"%d\n", | |
628 | pvr2_hdw_get_unit_number(sfp->channel.hdw)); | |
629 | } | |
630 | ||
631 | ||
632 | static void class_dev_create(struct pvr2_sysfs *sfp, | |
633 | struct pvr2_sysfs_class *class_ptr) | |
634 | { | |
635 | struct usb_device *usb_dev; | |
54bd5b66 | 636 | struct device *class_dev; |
3117beec MK |
637 | int ret; |
638 | ||
d855497e MI |
639 | usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); |
640 | if (!usb_dev) return; | |
ca545f7c | 641 | class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); |
d855497e | 642 | if (!class_dev) return; |
d855497e MI |
643 | |
644 | pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); | |
645 | ||
646 | class_dev->class = &class_ptr->class; | |
28c4a5e6 | 647 | |
730e92c9 MI |
648 | dev_set_name(class_dev, "%s", |
649 | pvr2_hdw_get_device_identifier(sfp->channel.hdw)); | |
d855497e | 650 | |
28c4a5e6 | 651 | class_dev->parent = get_device(&usb_dev->dev); |
d855497e MI |
652 | |
653 | sfp->class_dev = class_dev; | |
79510cdb | 654 | dev_set_drvdata(class_dev, sfp); |
54bd5b66 | 655 | ret = device_register(class_dev); |
3117beec | 656 | if (ret) { |
49844c29 MI |
657 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
658 | "device_register failed"); | |
a519d70e | 659 | put_device(class_dev); |
3117beec MK |
660 | return; |
661 | } | |
d855497e | 662 | |
12765517 | 663 | sysfs_attr_init(&sfp->attr_v4l_minor_number.attr); |
d855497e MI |
664 | sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number"; |
665 | sfp->attr_v4l_minor_number.attr.mode = S_IRUGO; | |
666 | sfp->attr_v4l_minor_number.show = v4l_minor_number_show; | |
a0fd1cb1 | 667 | sfp->attr_v4l_minor_number.store = NULL; |
54bd5b66 | 668 | ret = device_create_file(sfp->class_dev, |
08d41808 MI |
669 | &sfp->attr_v4l_minor_number); |
670 | if (ret < 0) { | |
49844c29 MI |
671 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
672 | "device_create_file error: %d", | |
673 | ret); | |
08d41808 MI |
674 | } else { |
675 | sfp->v4l_minor_number_created_ok = !0; | |
676 | } | |
3117beec | 677 | |
12765517 | 678 | sysfs_attr_init(&sfp->attr_v4l_radio_minor_number.attr); |
2fdf3d9c PK |
679 | sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number"; |
680 | sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; | |
681 | sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; | |
682 | sfp->attr_v4l_radio_minor_number.store = NULL; | |
54bd5b66 | 683 | ret = device_create_file(sfp->class_dev, |
2fdf3d9c PK |
684 | &sfp->attr_v4l_radio_minor_number); |
685 | if (ret < 0) { | |
49844c29 MI |
686 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
687 | "device_create_file error: %d", | |
688 | ret); | |
2fdf3d9c PK |
689 | } else { |
690 | sfp->v4l_radio_minor_number_created_ok = !0; | |
691 | } | |
692 | ||
12765517 | 693 | sysfs_attr_init(&sfp->attr_unit_number.attr); |
d855497e MI |
694 | sfp->attr_unit_number.attr.name = "unit_number"; |
695 | sfp->attr_unit_number.attr.mode = S_IRUGO; | |
696 | sfp->attr_unit_number.show = unit_number_show; | |
a0fd1cb1 | 697 | sfp->attr_unit_number.store = NULL; |
54bd5b66 | 698 | ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); |
08d41808 | 699 | if (ret < 0) { |
49844c29 MI |
700 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
701 | "device_create_file error: %d", | |
702 | ret); | |
08d41808 MI |
703 | } else { |
704 | sfp->unit_number_created_ok = !0; | |
705 | } | |
d855497e | 706 | |
12765517 | 707 | sysfs_attr_init(&sfp->attr_bus_info.attr); |
31a18547 MI |
708 | sfp->attr_bus_info.attr.name = "bus_info_str"; |
709 | sfp->attr_bus_info.attr.mode = S_IRUGO; | |
710 | sfp->attr_bus_info.show = bus_info_show; | |
711 | sfp->attr_bus_info.store = NULL; | |
54bd5b66 | 712 | ret = device_create_file(sfp->class_dev, |
31a18547 MI |
713 | &sfp->attr_bus_info); |
714 | if (ret < 0) { | |
49844c29 MI |
715 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
716 | "device_create_file error: %d", | |
717 | ret); | |
31a18547 MI |
718 | } else { |
719 | sfp->bus_info_created_ok = !0; | |
720 | } | |
721 | ||
12765517 | 722 | sysfs_attr_init(&sfp->attr_hdw_name.attr); |
78a47101 MI |
723 | sfp->attr_hdw_name.attr.name = "device_hardware_type"; |
724 | sfp->attr_hdw_name.attr.mode = S_IRUGO; | |
725 | sfp->attr_hdw_name.show = hdw_name_show; | |
726 | sfp->attr_hdw_name.store = NULL; | |
727 | ret = device_create_file(sfp->class_dev, | |
728 | &sfp->attr_hdw_name); | |
729 | if (ret < 0) { | |
49844c29 MI |
730 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
731 | "device_create_file error: %d", | |
732 | ret); | |
78a47101 MI |
733 | } else { |
734 | sfp->hdw_name_created_ok = !0; | |
735 | } | |
736 | ||
12765517 | 737 | sysfs_attr_init(&sfp->attr_hdw_desc.attr); |
78a47101 MI |
738 | sfp->attr_hdw_desc.attr.name = "device_hardware_description"; |
739 | sfp->attr_hdw_desc.attr.mode = S_IRUGO; | |
740 | sfp->attr_hdw_desc.show = hdw_desc_show; | |
741 | sfp->attr_hdw_desc.store = NULL; | |
742 | ret = device_create_file(sfp->class_dev, | |
743 | &sfp->attr_hdw_desc); | |
744 | if (ret < 0) { | |
49844c29 MI |
745 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
746 | "device_create_file error: %d", | |
747 | ret); | |
78a47101 MI |
748 | } else { |
749 | sfp->hdw_desc_created_ok = !0; | |
750 | } | |
751 | ||
d855497e MI |
752 | pvr2_sysfs_add_controls(sfp); |
753 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | |
754 | pvr2_sysfs_add_debugifc(sfp); | |
755 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | |
756 | } | |
757 | ||
758 | ||
759 | static void pvr2_sysfs_internal_check(struct pvr2_channel *chp) | |
760 | { | |
761 | struct pvr2_sysfs *sfp; | |
762 | sfp = container_of(chp,struct pvr2_sysfs,channel); | |
763 | if (!sfp->channel.mc_head->disconnect_flag) return; | |
764 | pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp); | |
765 | class_dev_destroy(sfp); | |
766 | pvr2_channel_done(&sfp->channel); | |
767 | kfree(sfp); | |
768 | } | |
769 | ||
770 | ||
771 | struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, | |
772 | struct pvr2_sysfs_class *class_ptr) | |
773 | { | |
774 | struct pvr2_sysfs *sfp; | |
ca545f7c | 775 | sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); |
d855497e | 776 | if (!sfp) return sfp; |
d855497e MI |
777 | pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); |
778 | pvr2_channel_init(&sfp->channel,mp); | |
779 | sfp->channel.check_func = pvr2_sysfs_internal_check; | |
780 | ||
781 | class_dev_create(sfp,class_ptr); | |
782 | return sfp; | |
783 | } | |
784 | ||
785 | ||
d855497e MI |
786 | |
787 | struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) | |
788 | { | |
789 | struct pvr2_sysfs_class *clp; | |
ca545f7c | 790 | clp = kzalloc(sizeof(*clp),GFP_KERNEL); |
d855497e | 791 | if (!clp) return clp; |
28c4a5e6 MI |
792 | pvr2_sysfs_trace("Creating and registering pvr2_sysfs_class id=%p", |
793 | clp); | |
d855497e MI |
794 | clp->class.name = "pvrusb2"; |
795 | clp->class.class_release = pvr2_sysfs_class_release; | |
54bd5b66 | 796 | clp->class.dev_release = pvr2_sysfs_release; |
d855497e MI |
797 | if (class_register(&clp->class)) { |
798 | pvr2_sysfs_trace( | |
799 | "Registration failed for pvr2_sysfs_class id=%p",clp); | |
800 | kfree(clp); | |
a0fd1cb1 | 801 | clp = NULL; |
d855497e MI |
802 | } |
803 | return clp; | |
804 | } | |
805 | ||
806 | ||
807 | void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) | |
808 | { | |
28c4a5e6 | 809 | pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); |
d855497e MI |
810 | class_unregister(&clp->class); |
811 | } | |
812 | ||
813 | ||
814 | #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC | |
c726b65d TP |
815 | static ssize_t debuginfo_show(struct device *class_dev, |
816 | struct device_attribute *attr, char *buf) | |
d855497e MI |
817 | { |
818 | struct pvr2_sysfs *sfp; | |
79510cdb | 819 | sfp = dev_get_drvdata(class_dev); |
d855497e MI |
820 | if (!sfp) return -EINVAL; |
821 | pvr2_hdw_trigger_module_log(sfp->channel.hdw); | |
822 | return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); | |
823 | } | |
824 | ||
825 | ||
c726b65d TP |
826 | static ssize_t debugcmd_show(struct device *class_dev, |
827 | struct device_attribute *attr, char *buf) | |
d855497e MI |
828 | { |
829 | struct pvr2_sysfs *sfp; | |
79510cdb | 830 | sfp = dev_get_drvdata(class_dev); |
d855497e MI |
831 | if (!sfp) return -EINVAL; |
832 | return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); | |
833 | } | |
834 | ||
835 | ||
54bd5b66 | 836 | static ssize_t debugcmd_store(struct device *class_dev, |
c726b65d | 837 | struct device_attribute *attr, |
54bd5b66 | 838 | const char *buf, size_t count) |
d855497e MI |
839 | { |
840 | struct pvr2_sysfs *sfp; | |
841 | int ret; | |
842 | ||
79510cdb | 843 | sfp = dev_get_drvdata(class_dev); |
d855497e MI |
844 | if (!sfp) return -EINVAL; |
845 | ||
846 | ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); | |
847 | if (ret < 0) return ret; | |
848 | return count; | |
849 | } | |
850 | #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ | |
851 | ||
852 | ||
853 | /* | |
854 | Stuff for Emacs to see, in order to encourage consistent editing style: | |
855 | *** Local Variables: *** | |
856 | *** mode: c *** | |
857 | *** fill-column: 75 *** | |
858 | *** tab-width: 8 *** | |
859 | *** c-basic-offset: 8 *** | |
860 | *** End: *** | |
861 | */ |