1 /*
2 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or (at
7 * your option) any later version.
8 */
9
10 #include <linux/miscdevice.h>
11 #include <linux/platform_device.h>
12 #include <linux/fs.h>
13 #include <linux/file.h>
14 #include <linux/mm.h>
15 #include <linux/list.h>
16 #include <linux/debugfs.h>
17 #include <linux/mempolicy.h>
18 #include <linux/sched.h>
19 #include <linux/dma-mapping.h>
20 #include <linux/io.h>
21 #include <linux/uaccess.h>
22 #include <linux/module.h>
23 #include <linux/soc/rockchip/rk_vendor_storage.h>
24 #include <linux/kthread.h>
25 #include <linux/delay.h>
26 #include <misc/rkflash_vendor_storage.h>
27 #include "../../mmc/host/rk_sdmmc_ops.h"
28
29 #define EMMC_IDB_PART_OFFSET 64
30 #define EMMC_SYS_PART_OFFSET 8064
31 #define EMMC_BOOT_PART_SIZE 1024
32 #define EMMC_VENDOR_PART_START (1024 * 7)
33 #define EMMC_VENDOR_PART_SIZE VENDOR_PART_SIZE
34 #define EMMC_VENDOR_PART_NUM 4
35 #define EMMC_VENDOR_TAG VENDOR_HEAD_TAG
36
37 #ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
38 #define READ_SECTOR_IO _IOW('r', 0x04, unsigned int)
39 #define WRITE_SECTOR_IO _IOW('r', 0x05, unsigned int)
40 #define END_WRITE_SECTOR_IO _IOW('r', 0x52, unsigned int)
41 #define GET_FLASH_INFO_IO _IOW('r', 0x1A, unsigned int)
42 #define GET_BAD_BLOCK_IO _IOW('r', 0x03, unsigned int)
43 #define GET_LOCK_FLAG_IO _IOW('r', 0x53, unsigned int)
44 #endif
45
46 static u8 *g_idb_buffer;
47 static struct vendor_info *g_vendor;
48 static DEFINE_MUTEX(vendor_ops_mutex);
49
emmc_vendor_ops(u8 * buffer,u32 addr,u32 n_sec,int write)50 static int emmc_vendor_ops(u8 *buffer, u32 addr, u32 n_sec, int write)
51 {
52 return rk_emmc_transfer(buffer, addr, n_sec << 9, write);
53 }
54
emmc_vendor_storage_init(void)55 static int emmc_vendor_storage_init(void)
56 {
57 u32 i, max_ver, max_index;
58 u8 *p_buf;
59
60 max_ver = 0;
61 max_index = 0;
62 for (i = 0; i < EMMC_VENDOR_PART_NUM; i++) {
63 /* read first 512 bytes */
64 p_buf = (u8 *)g_vendor;
65 if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START +
66 EMMC_VENDOR_PART_SIZE * i, 512, 0))
67 goto error_exit;
68 /* read last 512 bytes */
69 p_buf += (EMMC_VENDOR_PART_SIZE - 1) * 512;
70 if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START +
71 EMMC_VENDOR_PART_SIZE * (i + 1) - 1,
72 512, 0))
73 goto error_exit;
74
75 if (g_vendor->tag == EMMC_VENDOR_TAG &&
76 g_vendor->version2 == g_vendor->version) {
77 if (max_ver < g_vendor->version) {
78 max_index = i;
79 max_ver = g_vendor->version;
80 }
81 }
82 }
83 if (max_ver) {
84 if (emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
85 EMMC_VENDOR_PART_SIZE * max_index,
86 EMMC_VENDOR_PART_SIZE, 0))
87 goto error_exit;
88 } else {
89 memset((void *)g_vendor, 0, sizeof(*g_vendor));
90 g_vendor->version = 1;
91 g_vendor->tag = EMMC_VENDOR_TAG;
92 g_vendor->version2 = g_vendor->version;
93 g_vendor->free_offset = 0;
94 g_vendor->free_size = sizeof(g_vendor->data);
95 }
96 return 0;
97 error_exit:
98 return -1;
99 }
100
emmc_vendor_read(u32 id,void * pbuf,u32 size)101 static int emmc_vendor_read(u32 id, void *pbuf, u32 size)
102 {
103 u32 i;
104
105 if (!g_vendor)
106 return -ENOMEM;
107
108 for (i = 0; i < g_vendor->item_num; i++) {
109 if (g_vendor->item[i].id == id) {
110 if (size > g_vendor->item[i].size)
111 size = g_vendor->item[i].size;
112 memcpy(pbuf,
113 &g_vendor->data[g_vendor->item[i].offset],
114 size);
115 return size;
116 }
117 }
118 return (-1);
119 }
120
emmc_vendor_write(u32 id,void * pbuf,u32 size)121 static int emmc_vendor_write(u32 id, void *pbuf, u32 size)
122 {
123 u32 i, j, next_index, align_size, alloc_size, item_num;
124 u32 offset, next_size;
125 u8 *p_data;
126 struct vendor_item *item;
127 struct vendor_item *next_item;
128
129 if (!g_vendor)
130 return -ENOMEM;
131
132 p_data = g_vendor->data;
133 item_num = g_vendor->item_num;
134 align_size = ALIGN(size, 0x40); /* align to 64 bytes*/
135 next_index = g_vendor->next_index;
136 for (i = 0; i < item_num; i++) {
137 item = &g_vendor->item[i];
138 if (item->id == id) {
139 alloc_size = ALIGN(item->size, 0x40);
140 if (size > alloc_size) {
141 if (g_vendor->free_size < align_size)
142 return -1;
143 offset = item->offset;
144 for (j = i; j < item_num - 1; j++) {
145 item = &g_vendor->item[j];
146 next_item = &g_vendor->item[j + 1];
147 item->id = next_item->id;
148 item->size = next_item->size;
149 item->offset = offset;
150 next_size = ALIGN(next_item->size,
151 0x40);
152 memcpy(&p_data[offset],
153 &p_data[next_item->offset],
154 next_size);
155 offset += next_size;
156 }
157 item = &g_vendor->item[j];
158 item->id = id;
159 item->offset = offset;
160 item->size = size;
161 memcpy(&p_data[item->offset], pbuf, size);
162 g_vendor->free_offset = offset + align_size;
163 g_vendor->free_size -= (align_size -
164 alloc_size);
165 } else {
166 memcpy(&p_data[item->offset],
167 pbuf,
168 size);
169 g_vendor->item[i].size = size;
170 }
171 g_vendor->version++;
172 g_vendor->version2 = g_vendor->version;
173 g_vendor->next_index++;
174 if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
175 g_vendor->next_index = 0;
176 emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
177 EMMC_VENDOR_PART_SIZE * next_index,
178 EMMC_VENDOR_PART_SIZE, 1);
179 return 0;
180 }
181 }
182
183 if (g_vendor->free_size >= align_size) {
184 item = &g_vendor->item[g_vendor->item_num];
185 item->id = id;
186 item->offset = g_vendor->free_offset;
187 item->size = size;
188 g_vendor->free_offset += align_size;
189 g_vendor->free_size -= align_size;
190 memcpy(&g_vendor->data[item->offset], pbuf, size);
191 g_vendor->item_num++;
192 g_vendor->version++;
193 g_vendor->version2 = g_vendor->version;
194 g_vendor->next_index++;
195 if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
196 g_vendor->next_index = 0;
197 emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
198 EMMC_VENDOR_PART_SIZE * next_index,
199 EMMC_VENDOR_PART_SIZE, 1);
200 return 0;
201 }
202 return(-1);
203 }
204
205 #ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
id_blk_read_data(u32 index,u32 n_sec,u8 * buf)206 static int id_blk_read_data(u32 index, u32 n_sec, u8 *buf)
207 {
208 if (index + n_sec >= 1024 * 5)
209 return 0;
210 index = index + EMMC_IDB_PART_OFFSET;
211
212 return rk_emmc_transfer(buf, index, n_sec << 9, 0);
213 }
214
id_blk_write_data(u32 index,u32 n_sec,u8 * buf)215 static int id_blk_write_data(u32 index, u32 n_sec, u8 *buf)
216 {
217 if (index + n_sec >= 1024 * 5)
218 return 0;
219 index = index + EMMC_IDB_PART_OFFSET;
220
221 return rk_emmc_transfer(buf, index, n_sec << 9, 1);
222 }
223
emmc_write_idblock(u32 size,u8 * buf,u32 * id_blk_tbl)224 static int emmc_write_idblock(u32 size, u8 *buf, u32 *id_blk_tbl)
225 {
226 u32 i, totle_sec, j;
227 u32 totle_write_count = 0;
228 u32 *p_raw_data = (u32 *)buf;
229 u32 *p_check_buf = kmalloc(EMMC_BOOT_PART_SIZE * 512, GFP_KERNEL);
230
231 if (!p_check_buf)
232 return -ENOMEM;
233
234 totle_sec = (size + 511) >> 9;
235 if (totle_sec <= 8)
236 totle_sec = 8;
237
238 for (i = 0; i < 5; i++) {
239 memset(p_check_buf, 0, 512);
240 id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1,
241 (u8 *)p_check_buf);
242 id_blk_write_data(EMMC_BOOT_PART_SIZE * i + 1,
243 totle_sec - 1, buf + 512);
244 id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1, buf);
245 id_blk_read_data(EMMC_BOOT_PART_SIZE * i, totle_sec,
246 (u8 *)p_check_buf);
247 for (j = 0; j < totle_sec * 128; j++) {
248 if (p_check_buf[j] != p_raw_data[j]) {
249 memset(p_check_buf, 0, 512);
250 id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1,
251 (u8 *)p_check_buf);
252 break;
253 }
254 }
255 if (j >= totle_sec * 128)
256 totle_write_count++;
257 }
258 kfree(p_check_buf);
259 if (totle_write_count)
260 return 0;
261 return (-1);
262 }
263 #endif
264
vendor_storage_open(struct inode * inode,struct file * file)265 static int vendor_storage_open(struct inode *inode, struct file *file)
266 {
267 return 0;
268 }
269
vendor_storage_release(struct inode * inode,struct file * file)270 static int vendor_storage_release(struct inode *inode, struct file *file)
271 {
272 return 0;
273 }
274
275 #ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
276 static const u32 g_crc32_tbl[256] = {
277 0x00000000, 0x04c10db7, 0x09821b6e, 0x0d4316d9,
278 0x130436dc, 0x17c53b6b, 0x1a862db2, 0x1e472005,
279 0x26086db8, 0x22c9600f, 0x2f8a76d6, 0x2b4b7b61,
280 0x350c5b64, 0x31cd56d3, 0x3c8e400a, 0x384f4dbd,
281 0x4c10db70, 0x48d1d6c7, 0x4592c01e, 0x4153cda9,
282 0x5f14edac, 0x5bd5e01b, 0x5696f6c2, 0x5257fb75,
283 0x6a18b6c8, 0x6ed9bb7f, 0x639aada6, 0x675ba011,
284 0x791c8014, 0x7ddd8da3, 0x709e9b7a, 0x745f96cd,
285 0x9821b6e0, 0x9ce0bb57, 0x91a3ad8e, 0x9562a039,
286 0x8b25803c, 0x8fe48d8b, 0x82a79b52, 0x866696e5,
287 0xbe29db58, 0xbae8d6ef, 0xb7abc036, 0xb36acd81,
288 0xad2ded84, 0xa9ece033, 0xa4aff6ea, 0xa06efb5d,
289 0xd4316d90, 0xd0f06027, 0xddb376fe, 0xd9727b49,
290 0xc7355b4c, 0xc3f456fb, 0xceb74022, 0xca764d95,
291 0xf2390028, 0xf6f80d9f, 0xfbbb1b46, 0xff7a16f1,
292 0xe13d36f4, 0xe5fc3b43, 0xe8bf2d9a, 0xec7e202d,
293 0x34826077, 0x30436dc0, 0x3d007b19, 0x39c176ae,
294 0x278656ab, 0x23475b1c, 0x2e044dc5, 0x2ac54072,
295 0x128a0dcf, 0x164b0078, 0x1b0816a1, 0x1fc91b16,
296 0x018e3b13, 0x054f36a4, 0x080c207d, 0x0ccd2dca,
297 0x7892bb07, 0x7c53b6b0, 0x7110a069, 0x75d1adde,
298 0x6b968ddb, 0x6f57806c, 0x621496b5, 0x66d59b02,
299 0x5e9ad6bf, 0x5a5bdb08, 0x5718cdd1, 0x53d9c066,
300 0x4d9ee063, 0x495fedd4, 0x441cfb0d, 0x40ddf6ba,
301 0xaca3d697, 0xa862db20, 0xa521cdf9, 0xa1e0c04e,
302 0xbfa7e04b, 0xbb66edfc, 0xb625fb25, 0xb2e4f692,
303 0x8aabbb2f, 0x8e6ab698, 0x8329a041, 0x87e8adf6,
304 0x99af8df3, 0x9d6e8044, 0x902d969d, 0x94ec9b2a,
305 0xe0b30de7, 0xe4720050, 0xe9311689, 0xedf01b3e,
306 0xf3b73b3b, 0xf776368c, 0xfa352055, 0xfef42de2,
307 0xc6bb605f, 0xc27a6de8, 0xcf397b31, 0xcbf87686,
308 0xd5bf5683, 0xd17e5b34, 0xdc3d4ded, 0xd8fc405a,
309 0x6904c0ee, 0x6dc5cd59, 0x6086db80, 0x6447d637,
310 0x7a00f632, 0x7ec1fb85, 0x7382ed5c, 0x7743e0eb,
311 0x4f0cad56, 0x4bcda0e1, 0x468eb638, 0x424fbb8f,
312 0x5c089b8a, 0x58c9963d, 0x558a80e4, 0x514b8d53,
313 0x25141b9e, 0x21d51629, 0x2c9600f0, 0x28570d47,
314 0x36102d42, 0x32d120f5, 0x3f92362c, 0x3b533b9b,
315 0x031c7626, 0x07dd7b91, 0x0a9e6d48, 0x0e5f60ff,
316 0x101840fa, 0x14d94d4d, 0x199a5b94, 0x1d5b5623,
317 0xf125760e, 0xf5e47bb9, 0xf8a76d60, 0xfc6660d7,
318 0xe22140d2, 0xe6e04d65, 0xeba35bbc, 0xef62560b,
319 0xd72d1bb6, 0xd3ec1601, 0xdeaf00d8, 0xda6e0d6f,
320 0xc4292d6a, 0xc0e820dd, 0xcdab3604, 0xc96a3bb3,
321 0xbd35ad7e, 0xb9f4a0c9, 0xb4b7b610, 0xb076bba7,
322 0xae319ba2, 0xaaf09615, 0xa7b380cc, 0xa3728d7b,
323 0x9b3dc0c6, 0x9ffccd71, 0x92bfdba8, 0x967ed61f,
324 0x8839f61a, 0x8cf8fbad, 0x81bbed74, 0x857ae0c3,
325 0x5d86a099, 0x5947ad2e, 0x5404bbf7, 0x50c5b640,
326 0x4e829645, 0x4a439bf2, 0x47008d2b, 0x43c1809c,
327 0x7b8ecd21, 0x7f4fc096, 0x720cd64f, 0x76cddbf8,
328 0x688afbfd, 0x6c4bf64a, 0x6108e093, 0x65c9ed24,
329 0x11967be9, 0x1557765e, 0x18146087, 0x1cd56d30,
330 0x02924d35, 0x06534082, 0x0b10565b, 0x0fd15bec,
331 0x379e1651, 0x335f1be6, 0x3e1c0d3f, 0x3add0088,
332 0x249a208d, 0x205b2d3a, 0x2d183be3, 0x29d93654,
333 0xc5a71679, 0xc1661bce, 0xcc250d17, 0xc8e400a0,
334 0xd6a320a5, 0xd2622d12, 0xdf213bcb, 0xdbe0367c,
335 0xe3af7bc1, 0xe76e7676, 0xea2d60af, 0xeeec6d18,
336 0xf0ab4d1d, 0xf46a40aa, 0xf9295673, 0xfde85bc4,
337 0x89b7cd09, 0x8d76c0be, 0x8035d667, 0x84f4dbd0,
338 0x9ab3fbd5, 0x9e72f662, 0x9331e0bb, 0x97f0ed0c,
339 0xafbfa0b1, 0xab7ead06, 0xa63dbbdf, 0xa2fcb668,
340 0xbcbb966d, 0xb87a9bda, 0xb5398d03, 0xb1f880b4,
341 };
342
rk_crc_32(unsigned char * buf,u32 len)343 static u32 rk_crc_32(unsigned char *buf, u32 len)
344 {
345 u32 i;
346 u32 crc = 0;
347
348 for (i = 0; i < len; i++)
349 crc = (crc << 8) ^ g_crc32_tbl[(crc >> 24) ^ *buf++];
350 return crc;
351 }
352 #endif
353
vendor_storage_ioctl(struct file * file,unsigned int cmd,unsigned long arg)354 static long vendor_storage_ioctl(struct file *file, unsigned int cmd,
355 unsigned long arg)
356 {
357 long ret = -1;
358 int size;
359 struct RK_VENDOR_REQ *v_req;
360 u32 *page_buf;
361
362 page_buf = kmalloc(4096, GFP_KERNEL);
363 if (!page_buf)
364 return -ENOMEM;
365
366 mutex_lock(&vendor_ops_mutex);
367
368 v_req = (struct RK_VENDOR_REQ *)page_buf;
369
370 switch (cmd) {
371 case VENDOR_READ_IO:
372 {
373 if (copy_from_user(page_buf, (void __user *)arg, 8)) {
374 ret = -EFAULT;
375 break;
376 }
377 if (v_req->tag == VENDOR_REQ_TAG) {
378 size = emmc_vendor_read(v_req->id, v_req->data,
379 v_req->len);
380 if (size != -1) {
381 v_req->len = size;
382 ret = 0;
383 if (copy_to_user((void __user *)arg,
384 page_buf,
385 v_req->len + 8))
386 ret = -EFAULT;
387 }
388 }
389 } break;
390 case VENDOR_WRITE_IO:
391 {
392 if (copy_from_user(page_buf, (void __user *)arg, 8)) {
393 ret = -EFAULT;
394 break;
395 }
396 if (v_req->tag == VENDOR_REQ_TAG && (v_req->len < 4096 - 8)) {
397 if (copy_from_user(page_buf, (void __user *)arg,
398 v_req->len + 8)) {
399 ret = -EFAULT;
400 break;
401 }
402 ret = emmc_vendor_write(v_req->id,
403 v_req->data,
404 v_req->len);
405 }
406 } break;
407
408 #ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
409 case READ_SECTOR_IO:
410 {
411 if (copy_from_user(page_buf, (void __user *)arg, 512)) {
412 ret = -EFAULT;
413 goto exit;
414 }
415
416 size = page_buf[1];
417 if (size <= 8) {
418 id_blk_read_data(page_buf[0], size, (u8 *)page_buf);
419 if (copy_to_user((void __user *)arg, page_buf,
420 size * 512)) {
421 ret = -EFAULT;
422 goto exit;
423 }
424 } else {
425 ret = -EFAULT;
426 goto exit;
427 }
428 ret = 0;
429 } break;
430
431 case WRITE_SECTOR_IO:
432 {
433 if (copy_from_user(page_buf, (void __user *)arg, 4096)) {
434 ret = -EFAULT;
435 goto exit;
436 }
437 if (!g_idb_buffer) {
438 g_idb_buffer = kmalloc(4096 + EMMC_BOOT_PART_SIZE * 512,
439 GFP_KERNEL);
440 if (!g_idb_buffer) {
441 ret = -EFAULT;
442 goto exit;
443 }
444 }
445 if (page_buf[1] <= 4088 && page_buf[0] <=
446 (EMMC_BOOT_PART_SIZE * 512 - 4096)) {
447 memcpy(g_idb_buffer + page_buf[0], page_buf + 2,
448 page_buf[1]);
449 } else {
450 ret = -EFAULT;
451 goto exit;
452 }
453 ret = 0;
454 } break;
455
456 case END_WRITE_SECTOR_IO:
457 {
458 if (copy_from_user(page_buf, (void __user *)arg, 28)) {
459 ret = -EFAULT;
460 goto exit;
461 }
462 if (page_buf[0] <= (EMMC_BOOT_PART_SIZE * 512)) {
463 if (!g_idb_buffer) {
464 ret = -EFAULT;
465 goto exit;
466 }
467 if (page_buf[1] !=
468 rk_crc_32(g_idb_buffer, page_buf[0])) {
469 ret = -2;
470 goto exit;
471 }
472 ret = emmc_write_idblock(page_buf[0],
473 (u8 *)g_idb_buffer,
474 &page_buf[2]);
475 kfree(g_idb_buffer);
476 g_idb_buffer = NULL;
477 } else {
478 ret = -EFAULT;
479 goto exit;
480 }
481 ret = 0;
482 } break;
483
484 case GET_BAD_BLOCK_IO:
485 {
486 memset(page_buf, 0, 64);
487 if (copy_to_user((void __user *)arg, page_buf, 64)) {
488 ret = -EFAULT;
489 goto exit;
490 }
491 ret = 0;
492 } break;
493
494 case GET_LOCK_FLAG_IO:
495 {
496 page_buf[0] = 0;
497 if (copy_to_user((void __user *)arg, page_buf, 4)) {
498 ret = -EFAULT;
499 goto exit;
500 }
501 ret = 0;
502 } break;
503
504 case GET_FLASH_INFO_IO:
505 {
506 page_buf[0] = 0x00800000;
507 page_buf[1] = 0x00040400;
508 page_buf[2] = 0x00010028;
509 if (copy_to_user((void __user *)arg, page_buf, 11)) {
510 ret = -EFAULT;
511 goto exit;
512 }
513 ret = 0;
514 } break;
515 #endif
516
517 default:
518 ret = -EINVAL;
519 goto exit;
520 }
521 exit:
522 mutex_unlock(&vendor_ops_mutex);
523 kfree(page_buf);
524 return ret;
525 }
526
527 static const struct file_operations vendor_storage_fops = {
528 .open = vendor_storage_open,
529 .compat_ioctl = vendor_storage_ioctl,
530 .unlocked_ioctl = vendor_storage_ioctl,
531 .release = vendor_storage_release,
532 };
533
534 static struct miscdevice vender_storage_dev = {
535 .minor = MISC_DYNAMIC_MINOR,
536 .name = "vendor_storage",
537 .fops = &vendor_storage_fops,
538 };
539
vendor_init_thread(void * arg)540 static int vendor_init_thread(void *arg)
541 {
542 int ret;
543 unsigned long timeout = jiffies + 3 * HZ;
544
545 g_vendor = kmalloc(sizeof(*g_vendor), GFP_KERNEL | GFP_DMA);
546 if (!g_vendor)
547 return -ENOMEM;
548
549 do {
550 ret = emmc_vendor_storage_init();
551 if (!ret || time_after(jiffies, timeout))
552 break;
553 /* sleep wait emmc initialize completed */
554 msleep(100);
555 } while (1);
556
557 if (!ret) {
558 ret = misc_register(&vender_storage_dev);
559 rk_vendor_register(emmc_vendor_read, emmc_vendor_write);
560 } else {
561 kfree(g_vendor);
562 g_vendor = NULL;
563 }
564 pr_info("vendor storage:20190527 ret = %d\n", ret);
565 return ret;
566 }
567
vendor_storage_init(void)568 static int __init vendor_storage_init(void)
569 {
570 g_idb_buffer = NULL;
571 kthread_run(vendor_init_thread, (void *)NULL, "vendor_storage_init");
572 return 0;
573 }
574
vendor_storage_deinit(void)575 static __exit void vendor_storage_deinit(void)
576 {
577 if (g_vendor) {
578 misc_deregister(&vender_storage_dev);
579 kfree(g_vendor);
580 g_vendor = NULL;
581 }
582 }
583
584 device_initcall_sync(vendor_storage_init);
585 module_exit(vendor_storage_deinit);
586 MODULE_LICENSE("GPL");
587