1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Copyright(c) 2013 Mauro Carvalho Chehab |
4 | |
5 | #include "smscoreapi.h" |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/init.h> |
10 | #include <linux/debugfs.h> |
11 | #include <linux/spinlock.h> |
12 | #include <linux/usb.h> |
13 | |
14 | #include <media/dmxdev.h> |
15 | #include <media/dvbdev.h> |
16 | #include <media/dvb_demux.h> |
17 | #include <media/dvb_frontend.h> |
18 | |
19 | #include "smsdvb.h" |
20 | |
21 | static struct dentry *smsdvb_debugfs_usb_root; |
22 | |
23 | struct smsdvb_debugfs { |
24 | struct kref refcount; |
25 | spinlock_t lock; |
26 | |
27 | char stats_data[PAGE_SIZE]; |
28 | unsigned stats_count; |
29 | bool stats_was_read; |
30 | |
31 | wait_queue_head_t stats_queue; |
32 | }; |
33 | |
34 | static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data, |
35 | struct sms_stats *p) |
36 | { |
37 | int n = 0; |
38 | char *buf; |
39 | |
40 | spin_lock(lock: &debug_data->lock); |
41 | if (debug_data->stats_count) { |
42 | spin_unlock(lock: &debug_data->lock); |
43 | return; |
44 | } |
45 | |
46 | buf = debug_data->stats_data; |
47 | |
48 | n += sysfs_emit_at(buf, at: n, fmt: "is_rf_locked = %d\n" , p->is_rf_locked); |
49 | n += sysfs_emit_at(buf, at: n, fmt: "is_demod_locked = %d\n" , p->is_demod_locked); |
50 | n += sysfs_emit_at(buf, at: n, fmt: "is_external_lna_on = %d\n" , p->is_external_lna_on); |
51 | n += sysfs_emit_at(buf, at: n, fmt: "SNR = %d\n" , p->SNR); |
52 | n += sysfs_emit_at(buf, at: n, fmt: "ber = %d\n" , p->ber); |
53 | n += sysfs_emit_at(buf, at: n, fmt: "FIB_CRC = %d\n" , p->FIB_CRC); |
54 | n += sysfs_emit_at(buf, at: n, fmt: "ts_per = %d\n" , p->ts_per); |
55 | n += sysfs_emit_at(buf, at: n, fmt: "MFER = %d\n" , p->MFER); |
56 | n += sysfs_emit_at(buf, at: n, fmt: "RSSI = %d\n" , p->RSSI); |
57 | n += sysfs_emit_at(buf, at: n, fmt: "in_band_pwr = %d\n" , p->in_band_pwr); |
58 | n += sysfs_emit_at(buf, at: n, fmt: "carrier_offset = %d\n" , p->carrier_offset); |
59 | n += sysfs_emit_at(buf, at: n, fmt: "modem_state = %d\n" , p->modem_state); |
60 | n += sysfs_emit_at(buf, at: n, fmt: "frequency = %d\n" , p->frequency); |
61 | n += sysfs_emit_at(buf, at: n, fmt: "bandwidth = %d\n" , p->bandwidth); |
62 | n += sysfs_emit_at(buf, at: n, fmt: "transmission_mode = %d\n" , p->transmission_mode); |
63 | n += sysfs_emit_at(buf, at: n, fmt: "modem_state = %d\n" , p->modem_state); |
64 | n += sysfs_emit_at(buf, at: n, fmt: "guard_interval = %d\n" , p->guard_interval); |
65 | n += sysfs_emit_at(buf, at: n, fmt: "code_rate = %d\n" , p->code_rate); |
66 | n += sysfs_emit_at(buf, at: n, fmt: "lp_code_rate = %d\n" , p->lp_code_rate); |
67 | n += sysfs_emit_at(buf, at: n, fmt: "hierarchy = %d\n" , p->hierarchy); |
68 | n += sysfs_emit_at(buf, at: n, fmt: "constellation = %d\n" , p->constellation); |
69 | n += sysfs_emit_at(buf, at: n, fmt: "burst_size = %d\n" , p->burst_size); |
70 | n += sysfs_emit_at(buf, at: n, fmt: "burst_duration = %d\n" , p->burst_duration); |
71 | n += sysfs_emit_at(buf, at: n, fmt: "burst_cycle_time = %d\n" , p->burst_cycle_time); |
72 | n += sysfs_emit_at(buf, at: n, fmt: "calc_burst_cycle_time = %d\n" , p->calc_burst_cycle_time); |
73 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_rows = %d\n" , p->num_of_rows); |
74 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_padd_cols = %d\n" , p->num_of_padd_cols); |
75 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_punct_cols = %d\n" , p->num_of_punct_cols); |
76 | n += sysfs_emit_at(buf, at: n, fmt: "error_ts_packets = %d\n" , p->error_ts_packets); |
77 | n += sysfs_emit_at(buf, at: n, fmt: "total_ts_packets = %d\n" , p->total_ts_packets); |
78 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_valid_mpe_tlbs = %d\n" , p->num_of_valid_mpe_tlbs); |
79 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_invalid_mpe_tlbs = %d\n" , p->num_of_invalid_mpe_tlbs); |
80 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_corrected_mpe_tlbs = %d\n" , |
81 | p->num_of_corrected_mpe_tlbs); |
82 | n += sysfs_emit_at(buf, at: n, fmt: "ber_error_count = %d\n" , p->ber_error_count); |
83 | n += sysfs_emit_at(buf, at: n, fmt: "ber_bit_count = %d\n" , p->ber_bit_count); |
84 | n += sysfs_emit_at(buf, at: n, fmt: "sms_to_host_tx_errors = %d\n" , p->sms_to_host_tx_errors); |
85 | n += sysfs_emit_at(buf, at: n, fmt: "pre_ber = %d\n" , p->pre_ber); |
86 | n += sysfs_emit_at(buf, at: n, fmt: "cell_id = %d\n" , p->cell_id); |
87 | n += sysfs_emit_at(buf, at: n, fmt: "dvbh_srv_ind_hp = %d\n" , p->dvbh_srv_ind_hp); |
88 | n += sysfs_emit_at(buf, at: n, fmt: "dvbh_srv_ind_lp = %d\n" , p->dvbh_srv_ind_lp); |
89 | n += sysfs_emit_at(buf, at: n, fmt: "num_mpe_received = %d\n" , p->num_mpe_received); |
90 | |
91 | debug_data->stats_count = n; |
92 | spin_unlock(lock: &debug_data->lock); |
93 | wake_up(&debug_data->stats_queue); |
94 | } |
95 | |
96 | static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, |
97 | struct sms_isdbt_stats *p) |
98 | { |
99 | int i, n = 0; |
100 | char *buf; |
101 | |
102 | spin_lock(lock: &debug_data->lock); |
103 | if (debug_data->stats_count) { |
104 | spin_unlock(lock: &debug_data->lock); |
105 | return; |
106 | } |
107 | |
108 | buf = debug_data->stats_data; |
109 | |
110 | n += sysfs_emit_at(buf, at: n, fmt: "statistics_type = %d\t" , p->statistics_type); |
111 | n += sysfs_emit_at(buf, at: n, fmt: "full_size = %d\n" , p->full_size); |
112 | |
113 | n += sysfs_emit_at(buf, at: n, fmt: "is_rf_locked = %d\t\t" , p->is_rf_locked); |
114 | n += sysfs_emit_at(buf, at: n, fmt: "is_demod_locked = %d\t" , p->is_demod_locked); |
115 | n += sysfs_emit_at(buf, at: n, fmt: "is_external_lna_on = %d\n" , p->is_external_lna_on); |
116 | n += sysfs_emit_at(buf, at: n, fmt: "SNR = %d dB\t\t" , p->SNR); |
117 | n += sysfs_emit_at(buf, at: n, fmt: "RSSI = %d dBm\t\t" , p->RSSI); |
118 | n += sysfs_emit_at(buf, at: n, fmt: "in_band_pwr = %d dBm\n" , p->in_band_pwr); |
119 | n += sysfs_emit_at(buf, at: n, fmt: "carrier_offset = %d\t" , p->carrier_offset); |
120 | n += sysfs_emit_at(buf, at: n, fmt: "bandwidth = %d\t\t" , p->bandwidth); |
121 | n += sysfs_emit_at(buf, at: n, fmt: "frequency = %d Hz\n" , p->frequency); |
122 | n += sysfs_emit_at(buf, at: n, fmt: "transmission_mode = %d\t" , p->transmission_mode); |
123 | n += sysfs_emit_at(buf, at: n, fmt: "modem_state = %d\t\t" , p->modem_state); |
124 | n += sysfs_emit_at(buf, at: n, fmt: "guard_interval = %d\n" , p->guard_interval); |
125 | n += sysfs_emit_at(buf, at: n, fmt: "system_type = %d\t\t" , p->system_type); |
126 | n += sysfs_emit_at(buf, at: n, fmt: "partial_reception = %d\t" , p->partial_reception); |
127 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_layers = %d\n" , p->num_of_layers); |
128 | n += sysfs_emit_at(buf, at: n, fmt: "sms_to_host_tx_errors = %d\n" , p->sms_to_host_tx_errors); |
129 | |
130 | for (i = 0; i < 3; i++) { |
131 | if (p->layer_info[i].number_of_segments < 1 || |
132 | p->layer_info[i].number_of_segments > 13) |
133 | continue; |
134 | |
135 | n += sysfs_emit_at(buf, at: n, fmt: "\nLayer %d\n" , i); |
136 | n += sysfs_emit_at(buf, at: n, fmt: "\tcode_rate = %d\t" , p->layer_info[i].code_rate); |
137 | n += sysfs_emit_at(buf, at: n, fmt: "constellation = %d\n" , p->layer_info[i].constellation); |
138 | n += sysfs_emit_at(buf, at: n, fmt: "\tber = %-5d\t" , p->layer_info[i].ber); |
139 | n += sysfs_emit_at(buf, at: n, fmt: "\tber_error_count = %-5d\t" , |
140 | p->layer_info[i].ber_error_count); |
141 | n += sysfs_emit_at(buf, at: n, fmt: "ber_bit_count = %-5d\n" , |
142 | p->layer_info[i].ber_bit_count); |
143 | n += sysfs_emit_at(buf, at: n, fmt: "\tpre_ber = %-5d\t" , p->layer_info[i].pre_ber); |
144 | n += sysfs_emit_at(buf, at: n, fmt: "\tts_per = %-5d\n" , p->layer_info[i].ts_per); |
145 | n += sysfs_emit_at(buf, at: n, fmt: "\terror_ts_packets = %-5d\t" , |
146 | p->layer_info[i].error_ts_packets); |
147 | n += sysfs_emit_at(buf, at: n, fmt: "total_ts_packets = %-5d\t" , |
148 | p->layer_info[i].total_ts_packets); |
149 | n += sysfs_emit_at(buf, at: n, fmt: "ti_ldepth_i = %d\n" , p->layer_info[i].ti_ldepth_i); |
150 | n += sysfs_emit_at(buf, at: n, fmt: "\tnumber_of_segments = %d\t" , |
151 | p->layer_info[i].number_of_segments); |
152 | n += sysfs_emit_at(buf, at: n, fmt: "tmcc_errors = %d\n" , p->layer_info[i].tmcc_errors); |
153 | } |
154 | |
155 | debug_data->stats_count = n; |
156 | spin_unlock(lock: &debug_data->lock); |
157 | wake_up(&debug_data->stats_queue); |
158 | } |
159 | |
160 | static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, |
161 | struct sms_isdbt_stats_ex *p) |
162 | { |
163 | int i, n = 0; |
164 | char *buf; |
165 | |
166 | spin_lock(lock: &debug_data->lock); |
167 | if (debug_data->stats_count) { |
168 | spin_unlock(lock: &debug_data->lock); |
169 | return; |
170 | } |
171 | |
172 | buf = debug_data->stats_data; |
173 | |
174 | n += sysfs_emit_at(buf, at: n, fmt: "statistics_type = %d\t" , p->statistics_type); |
175 | n += sysfs_emit_at(buf, at: n, fmt: "full_size = %d\n" , p->full_size); |
176 | |
177 | n += sysfs_emit_at(buf, at: n, fmt: "is_rf_locked = %d\t\t" , p->is_rf_locked); |
178 | n += sysfs_emit_at(buf, at: n, fmt: "is_demod_locked = %d\t" , p->is_demod_locked); |
179 | n += sysfs_emit_at(buf, at: n, fmt: "is_external_lna_on = %d\n" , p->is_external_lna_on); |
180 | n += sysfs_emit_at(buf, at: n, fmt: "SNR = %d dB\t\t" , p->SNR); |
181 | n += sysfs_emit_at(buf, at: n, fmt: "RSSI = %d dBm\t\t" , p->RSSI); |
182 | n += sysfs_emit_at(buf, at: n, fmt: "in_band_pwr = %d dBm\n" , p->in_band_pwr); |
183 | n += sysfs_emit_at(buf, at: n, fmt: "carrier_offset = %d\t" , p->carrier_offset); |
184 | n += sysfs_emit_at(buf, at: n, fmt: "bandwidth = %d\t\t" , p->bandwidth); |
185 | n += sysfs_emit_at(buf, at: n, fmt: "frequency = %d Hz\n" , p->frequency); |
186 | n += sysfs_emit_at(buf, at: n, fmt: "transmission_mode = %d\t" , p->transmission_mode); |
187 | n += sysfs_emit_at(buf, at: n, fmt: "modem_state = %d\t\t" , p->modem_state); |
188 | n += sysfs_emit_at(buf, at: n, fmt: "guard_interval = %d\n" , p->guard_interval); |
189 | n += sysfs_emit_at(buf, at: n, fmt: "system_type = %d\t\t" , p->system_type); |
190 | n += sysfs_emit_at(buf, at: n, fmt: "partial_reception = %d\t" , p->partial_reception); |
191 | n += sysfs_emit_at(buf, at: n, fmt: "num_of_layers = %d\n" , p->num_of_layers); |
192 | n += sysfs_emit_at(buf, at: n, fmt: "segment_number = %d\t" , p->segment_number); |
193 | n += sysfs_emit_at(buf, at: n, fmt: "tune_bw = %d\n" , p->tune_bw); |
194 | |
195 | for (i = 0; i < 3; i++) { |
196 | if (p->layer_info[i].number_of_segments < 1 || |
197 | p->layer_info[i].number_of_segments > 13) |
198 | continue; |
199 | |
200 | n += sysfs_emit_at(buf, at: n, fmt: "\nLayer %d\n" , i); |
201 | n += sysfs_emit_at(buf, at: n, fmt: "\tcode_rate = %d\t" , p->layer_info[i].code_rate); |
202 | n += sysfs_emit_at(buf, at: n, fmt: "constellation = %d\n" , p->layer_info[i].constellation); |
203 | n += sysfs_emit_at(buf, at: n, fmt: "\tber = %-5d\t" , p->layer_info[i].ber); |
204 | n += sysfs_emit_at(buf, at: n, fmt: "\tber_error_count = %-5d\t" , |
205 | p->layer_info[i].ber_error_count); |
206 | n += sysfs_emit_at(buf, at: n, fmt: "ber_bit_count = %-5d\n" , |
207 | p->layer_info[i].ber_bit_count); |
208 | n += sysfs_emit_at(buf, at: n, fmt: "\tpre_ber = %-5d\t" , p->layer_info[i].pre_ber); |
209 | n += sysfs_emit_at(buf, at: n, fmt: "\tts_per = %-5d\n" , p->layer_info[i].ts_per); |
210 | n += sysfs_emit_at(buf, at: n, fmt: "\terror_ts_packets = %-5d\t" , |
211 | p->layer_info[i].error_ts_packets); |
212 | n += sysfs_emit_at(buf, at: n, fmt: "total_ts_packets = %-5d\t" , |
213 | p->layer_info[i].total_ts_packets); |
214 | n += sysfs_emit_at(buf, at: n, fmt: "ti_ldepth_i = %d\n" , p->layer_info[i].ti_ldepth_i); |
215 | n += sysfs_emit_at(buf, at: n, fmt: "\tnumber_of_segments = %d\t" , |
216 | p->layer_info[i].number_of_segments); |
217 | n += sysfs_emit_at(buf, at: n, fmt: "tmcc_errors = %d\n" , p->layer_info[i].tmcc_errors); |
218 | } |
219 | |
220 | |
221 | debug_data->stats_count = n; |
222 | spin_unlock(lock: &debug_data->lock); |
223 | |
224 | wake_up(&debug_data->stats_queue); |
225 | } |
226 | |
227 | static int smsdvb_stats_open(struct inode *inode, struct file *file) |
228 | { |
229 | struct smsdvb_client_t *client = inode->i_private; |
230 | struct smsdvb_debugfs *debug_data = client->debug_data; |
231 | |
232 | kref_get(kref: &debug_data->refcount); |
233 | |
234 | spin_lock(lock: &debug_data->lock); |
235 | debug_data->stats_count = 0; |
236 | debug_data->stats_was_read = false; |
237 | spin_unlock(lock: &debug_data->lock); |
238 | |
239 | file->private_data = debug_data; |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | static void smsdvb_debugfs_data_release(struct kref *ref) |
245 | { |
246 | struct smsdvb_debugfs *debug_data; |
247 | |
248 | debug_data = container_of(ref, struct smsdvb_debugfs, refcount); |
249 | kfree(objp: debug_data); |
250 | } |
251 | |
252 | static int smsdvb_stats_wait_read(struct smsdvb_debugfs *debug_data) |
253 | { |
254 | int rc = 1; |
255 | |
256 | spin_lock(lock: &debug_data->lock); |
257 | |
258 | if (debug_data->stats_was_read) |
259 | goto exit; |
260 | |
261 | rc = debug_data->stats_count; |
262 | |
263 | exit: |
264 | spin_unlock(lock: &debug_data->lock); |
265 | return rc; |
266 | } |
267 | |
268 | static __poll_t smsdvb_stats_poll(struct file *file, poll_table *wait) |
269 | { |
270 | struct smsdvb_debugfs *debug_data = file->private_data; |
271 | int rc; |
272 | |
273 | kref_get(kref: &debug_data->refcount); |
274 | |
275 | poll_wait(filp: file, wait_address: &debug_data->stats_queue, p: wait); |
276 | |
277 | rc = smsdvb_stats_wait_read(debug_data); |
278 | kref_put(kref: &debug_data->refcount, release: smsdvb_debugfs_data_release); |
279 | |
280 | return rc > 0 ? EPOLLIN | EPOLLRDNORM : 0; |
281 | } |
282 | |
283 | static ssize_t smsdvb_stats_read(struct file *file, char __user *user_buf, |
284 | size_t nbytes, loff_t *ppos) |
285 | { |
286 | int rc = 0, len; |
287 | struct smsdvb_debugfs *debug_data = file->private_data; |
288 | |
289 | kref_get(kref: &debug_data->refcount); |
290 | |
291 | if (file->f_flags & O_NONBLOCK) { |
292 | rc = smsdvb_stats_wait_read(debug_data); |
293 | if (!rc) { |
294 | rc = -EWOULDBLOCK; |
295 | goto ret; |
296 | } |
297 | } else { |
298 | rc = wait_event_interruptible(debug_data->stats_queue, |
299 | smsdvb_stats_wait_read(debug_data)); |
300 | if (rc < 0) |
301 | goto ret; |
302 | } |
303 | |
304 | if (debug_data->stats_was_read) { |
305 | rc = 0; /* EOF */ |
306 | goto ret; |
307 | } |
308 | |
309 | len = debug_data->stats_count - *ppos; |
310 | if (len >= 0) |
311 | rc = simple_read_from_buffer(to: user_buf, count: nbytes, ppos, |
312 | from: debug_data->stats_data, available: len); |
313 | else |
314 | rc = 0; |
315 | |
316 | if (*ppos >= debug_data->stats_count) { |
317 | spin_lock(lock: &debug_data->lock); |
318 | debug_data->stats_was_read = true; |
319 | spin_unlock(lock: &debug_data->lock); |
320 | } |
321 | ret: |
322 | kref_put(kref: &debug_data->refcount, release: smsdvb_debugfs_data_release); |
323 | return rc; |
324 | } |
325 | |
326 | static int smsdvb_stats_release(struct inode *inode, struct file *file) |
327 | { |
328 | struct smsdvb_debugfs *debug_data = file->private_data; |
329 | |
330 | spin_lock(lock: &debug_data->lock); |
331 | debug_data->stats_was_read = true; /* return EOF to read() */ |
332 | spin_unlock(lock: &debug_data->lock); |
333 | wake_up_interruptible_sync(&debug_data->stats_queue); |
334 | |
335 | kref_put(kref: &debug_data->refcount, release: smsdvb_debugfs_data_release); |
336 | file->private_data = NULL; |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static const struct file_operations debugfs_stats_ops = { |
342 | .open = smsdvb_stats_open, |
343 | .poll = smsdvb_stats_poll, |
344 | .read = smsdvb_stats_read, |
345 | .release = smsdvb_stats_release, |
346 | .llseek = generic_file_llseek, |
347 | }; |
348 | |
349 | /* |
350 | * Functions used by smsdvb, in order to create the interfaces |
351 | */ |
352 | |
353 | int smsdvb_debugfs_create(struct smsdvb_client_t *client) |
354 | { |
355 | struct smscore_device_t *coredev = client->coredev; |
356 | struct smsdvb_debugfs *debug_data; |
357 | |
358 | if (!smsdvb_debugfs_usb_root || !coredev->is_usb_device) |
359 | return -ENODEV; |
360 | |
361 | debug_data = kzalloc(size: sizeof(*client->debug_data), GFP_KERNEL); |
362 | if (!debug_data) |
363 | return -ENOMEM; |
364 | |
365 | client->debugfs = debugfs_create_dir(name: coredev->devpath, |
366 | parent: smsdvb_debugfs_usb_root); |
367 | |
368 | debugfs_create_file(name: "stats" , S_IRUGO | S_IWUSR, parent: client->debugfs, |
369 | data: client, fops: &debugfs_stats_ops); |
370 | |
371 | client->debug_data = debug_data; |
372 | client->prt_dvb_stats = smsdvb_print_dvb_stats; |
373 | client->prt_isdb_stats = smsdvb_print_isdb_stats; |
374 | client->prt_isdb_stats_ex = smsdvb_print_isdb_stats_ex; |
375 | |
376 | init_waitqueue_head(&debug_data->stats_queue); |
377 | spin_lock_init(&debug_data->lock); |
378 | kref_init(kref: &debug_data->refcount); |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | void smsdvb_debugfs_release(struct smsdvb_client_t *client) |
384 | { |
385 | if (!client->debugfs) |
386 | return; |
387 | |
388 | client->prt_dvb_stats = NULL; |
389 | client->prt_isdb_stats = NULL; |
390 | client->prt_isdb_stats_ex = NULL; |
391 | |
392 | debugfs_remove_recursive(dentry: client->debugfs); |
393 | kref_put(kref: &client->debug_data->refcount, release: smsdvb_debugfs_data_release); |
394 | |
395 | client->debug_data = NULL; |
396 | client->debugfs = NULL; |
397 | } |
398 | |
399 | void smsdvb_debugfs_register(void) |
400 | { |
401 | struct dentry *d; |
402 | |
403 | /* |
404 | * FIXME: This was written to debug Siano USB devices. So, it creates |
405 | * the debugfs node under <debugfs>/usb. |
406 | * A similar logic would be needed for Siano sdio devices, but, in that |
407 | * case, usb_debug_root is not a good choice. |
408 | * |
409 | * Perhaps the right fix here would be to create another sysfs root |
410 | * node for sdio-based boards, but this may need some logic at sdio |
411 | * subsystem. |
412 | */ |
413 | d = debugfs_create_dir(name: "smsdvb" , parent: usb_debug_root); |
414 | if (IS_ERR_OR_NULL(ptr: d)) { |
415 | pr_err("Couldn't create sysfs node for smsdvb\n" ); |
416 | return; |
417 | } |
418 | smsdvb_debugfs_usb_root = d; |
419 | } |
420 | |
421 | void smsdvb_debugfs_unregister(void) |
422 | { |
423 | if (!smsdvb_debugfs_usb_root) |
424 | return; |
425 | debugfs_remove_recursive(dentry: smsdvb_debugfs_usb_root); |
426 | smsdvb_debugfs_usb_root = NULL; |
427 | } |
428 | |