From 9cc2771daa1a9c17b3040d5bd34ad28e365c231b Mon Sep 17 00:00:00 2001 From: glevand Date: Mon, 3 Feb 2014 12:07:39 +0100 Subject: [PATCH] initial import --- 0010-ps3stor-multiple-regions.patch | 481 +++ 0020-ps3fb-use-fifo.patch | 537 +++ 0030-ps3flash.patch | 1076 +++++ 0035-ps3-partition.patch | 151 + 0040-ps3sysmgr-lpar-reboot.patch | 11 + 0050-ps3sysmgr-char-device.patch | 174 + 0060-ps3avmgr-char-device.patch | 143 + 0070-ps3dispmgr.patch | 328 ++ 0080-ps3rom-vendor-specific-command.patch | 63 + 0090-spu-enum-shared-param.patch | 48 + 0100-lv1call-repo-node-lparid-param.patch | 16 + 0110-lv1call-add-hvcalls-114-115.patch | 11 + 0120-lv1call-add-storage-region-hvcalls.patch | 13 + 0130-ps3physmem.patch | 211 + 0140-ps3strgmngr.patch | 565 +++ 0150-ps3jupiter.patch | 4872 +++++++++++++++++++++++ 0160-gelic-disable-eurus-ctrl-iface.patch | 78 + 0170-gelic-wireless-print-cmd-status.patch | 11 + 0180-lv1call-add-undocumented-spe-hvcalls.patch | 72 + 0190-export-spe-irq-setup-destroy.patch | 18 + 0200-export-event-receive-port-destroy.patch | 10 + 0210-ps3encdec.patch | 499 +++ 0220-spuisofs.patch | 1095 +++++ 0230-spuldrfs.patch | 1109 ++++++ 0240-ps3lv1call.patch | 247 ++ 0250-lv1call-add-debug-console-hvcalls.patch | 14 + 0260-udbg-lv1-console.patch | 123 + 27 files changed, 11976 insertions(+) create mode 100644 0010-ps3stor-multiple-regions.patch create mode 100644 0020-ps3fb-use-fifo.patch create mode 100644 0030-ps3flash.patch create mode 100644 0035-ps3-partition.patch create mode 100644 0040-ps3sysmgr-lpar-reboot.patch create mode 100644 0050-ps3sysmgr-char-device.patch create mode 100644 0060-ps3avmgr-char-device.patch create mode 100644 0070-ps3dispmgr.patch create mode 100644 0080-ps3rom-vendor-specific-command.patch create mode 100644 0090-spu-enum-shared-param.patch create mode 100644 0100-lv1call-repo-node-lparid-param.patch create mode 100644 0110-lv1call-add-hvcalls-114-115.patch create mode 100644 0120-lv1call-add-storage-region-hvcalls.patch create mode 100644 0130-ps3physmem.patch create mode 100644 0140-ps3strgmngr.patch create mode 100644 0150-ps3jupiter.patch create mode 100644 0160-gelic-disable-eurus-ctrl-iface.patch create mode 100644 0170-gelic-wireless-print-cmd-status.patch create mode 100644 0180-lv1call-add-undocumented-spe-hvcalls.patch create mode 100644 0190-export-spe-irq-setup-destroy.patch create mode 100644 0200-export-event-receive-port-destroy.patch create mode 100644 0210-ps3encdec.patch create mode 100644 0220-spuisofs.patch create mode 100644 0230-spuldrfs.patch create mode 100644 0240-ps3lv1call.patch create mode 100644 0250-lv1call-add-debug-console-hvcalls.patch create mode 100644 0260-udbg-lv1-console.patch diff --git a/0010-ps3stor-multiple-regions.patch b/0010-ps3stor-multiple-regions.patch new file mode 100644 index 0000000..10d29f4 --- /dev/null +++ b/0010-ps3stor-multiple-regions.patch @@ -0,0 +1,481 @@ +--- a/arch/powerpc/include/asm/ps3stor.h 2012-01-03 19:41:27.000000000 +0100 ++++ b/arch/powerpc/include/asm/ps3stor.h 2012-01-05 16:38:31.006804326 +0100 +@@ -30,6 +30,7 @@ + unsigned int id; + u64 start; + u64 size; ++ u64 flags; + }; + + struct ps3_storage_device { +@@ -50,7 +51,6 @@ + + unsigned int num_regions; + unsigned long accessible_regions; +- unsigned int region_idx; /* first accessible region */ + struct ps3_storage_region regions[0]; /* Must be last */ + }; + +@@ -62,8 +62,8 @@ + extern int ps3stor_setup(struct ps3_storage_device *dev, + irq_handler_t handler); + extern void ps3stor_teardown(struct ps3_storage_device *dev); +-extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, +- u64 start_sector, u64 sectors, ++extern u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, unsigned int region_idx, ++ u64 start_sector, u64 sectors, u64 flags, + int write); + extern u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, + u64 arg1, u64 arg2, u64 arg3, u64 arg4); +--- a/drivers/ps3/ps3stor_lib.c 2012-01-05 16:42:51.670823329 +0100 ++++ b/drivers/ps3/ps3stor_lib.c 2012-01-05 16:44:22.208906179 +0100 +@@ -101,9 +101,8 @@ + "%s:%u: checking accessibility of region %u\n", + __func__, __LINE__, i); + +- dev->region_idx = i; +- res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1, +- 0); ++ res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, i, 0, 1, ++ dev->regions[i].flags, 0); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: read failed, " + "region %u is not accessible\n", __func__, +@@ -115,6 +114,10 @@ + __func__, __LINE__, i); + set_bit(i, &dev->accessible_regions); + ++ dev_info(&dev->sbd.core, ++ "accessible region %u start %llu size %llu\n", ++ i, dev->regions[i].start, dev->regions[i].size); ++ + /* We can access at least one region */ + error = 0; + } +@@ -124,14 +127,8 @@ + n = hweight_long(dev->accessible_regions); + if (n > 1) + dev_info(&dev->sbd.core, +- "%s:%u: %lu accessible regions found. Only the first " +- "one will be used\n", ++ "%s:%u: %lu accessible regions found\n", + __func__, __LINE__, n); +- dev->region_idx = __ffs(dev->accessible_regions); +- dev_info(&dev->sbd.core, +- "First accessible region has index %u start %llu size %llu\n", +- dev->region_idx, dev->regions[dev->region_idx].start, +- dev->regions[dev->region_idx].size); + + return 0; + } +@@ -265,17 +262,19 @@ + * ps3stor_read_write_sectors - read/write from/to a storage device + * @dev: Pointer to a struct ps3_storage_device + * @lpar: HV logical partition address ++ * @region_idx: Region index + * @start_sector: First sector to read/write + * @sectors: Number of sectors to read/write ++ * @flags: Flags + * @write: Flag indicating write (non-zero) or read (zero) + * + * Returns 0 for success, -1 in case of failure to submit the command, or + * an LV1 status value in case of other errors + */ +-u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, +- u64 start_sector, u64 sectors, int write) ++u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, unsigned int region_idx, ++ u64 start_sector, u64 sectors, u64 flags, int write) + { +- unsigned int region_id = dev->regions[dev->region_idx].id; ++ unsigned int region_id = dev->regions[region_idx].id; + const char *op = write ? "write" : "read"; + int res; + +@@ -284,10 +283,10 @@ + + init_completion(&dev->done); + res = write ? lv1_storage_write(dev->sbd.dev_id, region_id, +- start_sector, sectors, 0, lpar, ++ start_sector, sectors, flags, lpar, + &dev->tag) + : lv1_storage_read(dev->sbd.dev_id, region_id, +- start_sector, sectors, 0, lpar, ++ start_sector, sectors, flags, lpar, + &dev->tag); + if (res) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, +--- a/drivers/block/ps3disk.c 2013-02-14 11:22:05.716017676 +0100 ++++ b/drivers/block/ps3disk.c 2013-02-14 11:19:47.200033102 +0100 +@@ -32,7 +32,8 @@ + + #define BOUNCE_SIZE (64*1024) + +-#define PS3DISK_MAX_DISKS 16 ++#define PS3DISK_MAX_NUM_REGS 8 ++ + #define PS3DISK_MINORS 16 + + +@@ -41,12 +42,13 @@ + + struct ps3disk_private { + spinlock_t lock; /* Request queue spinlock */ +- struct request_queue *queue; +- struct gendisk *gendisk; + unsigned int blocking_factor; + struct request *req; + u64 raw_capacity; + unsigned char model[ATA_ID_PROD_LEN+1]; ++ struct gendisk *gendisk[PS3DISK_MAX_NUM_REGS]; ++ struct request_queue *queue[PS3DISK_MAX_NUM_REGS]; ++ int next_queue; + }; + + +@@ -88,6 +90,13 @@ + .owner = THIS_MODULE, + }; + ++static unsigned int region_flags[] = ++{ ++ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ++}; ++module_param_array(region_flags, uint, NULL, S_IRUGO); ++MODULE_PARM_DESC(region_flags, "Region flags"); ++ + + static void ps3disk_scatter_gather(struct ps3_storage_device *dev, + struct request *req, int gather) +@@ -126,7 +135,9 @@ + int write = rq_data_dir(req), res; + const char *op = write ? "write" : "read"; + u64 start_sector, sectors; +- unsigned int region_id = dev->regions[dev->region_idx].id; ++ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3DISK_MINORS; ++ unsigned int region_id = dev->regions[region_idx].id; ++ unsigned int region_flags = dev->regions[region_idx].flags; + + #ifdef DEBUG + unsigned int n = 0; +@@ -149,11 +160,11 @@ + ps3disk_scatter_gather(dev, req, 1); + + res = lv1_storage_write(dev->sbd.dev_id, region_id, +- start_sector, sectors, 0, ++ start_sector, sectors, region_flags, + dev->bounce_lpar, &dev->tag); + } else { + res = lv1_storage_read(dev->sbd.dev_id, region_id, +- start_sector, sectors, 0, ++ start_sector, sectors, region_flags, + dev->bounce_lpar, &dev->tag); + } + if (res) { +@@ -232,6 +243,8 @@ + int res, read, error; + u64 tag, status; + const char *op; ++ struct request_queue *q; ++ int old_queue; + + res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); + +@@ -279,7 +292,20 @@ + spin_lock(&priv->lock); + __blk_end_request_all(req, error); + priv->req = NULL; +- ps3disk_do_request(dev, priv->queue); ++ old_queue = priv->next_queue; ++ do { ++ q = priv->queue[priv->next_queue]; ++ ++ priv->next_queue++; ++ if (priv->next_queue >= dev->num_regions) ++ priv->next_queue = 0; ++ ++ if (q) { ++ ps3disk_do_request(dev, q); ++ if (priv->req) ++ break; ++ } ++ } while (old_queue != priv->next_queue); + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +@@ -397,19 +423,17 @@ + return 0; + } + +-static unsigned long ps3disk_mask; +- +-static DEFINE_MUTEX(ps3disk_mask_mutex); +- + static int ps3disk_probe(struct ps3_system_bus_device *_dev) + { + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct ps3disk_private *priv; + int error; +- unsigned int devidx; ++ unsigned int regidx, devidx; + struct request_queue *queue; + struct gendisk *gendisk; + ++ BUG_ON(dev->num_regions > PS3DISK_MAX_NUM_REGS); ++ + if (dev->blk_size < 512) { + dev_err(&dev->sbd.core, + "%s:%u: cannot handle block size %llu\n", __func__, +@@ -417,18 +441,6 @@ + return -EINVAL; + } + +- BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG); +- mutex_lock(&ps3disk_mask_mutex); +- devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS); +- if (devidx >= PS3DISK_MAX_DISKS) { +- dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__, +- __LINE__); +- mutex_unlock(&ps3disk_mask_mutex); +- return -ENOSPC; +- } +- __set_bit(devidx, &ps3disk_mask); +- mutex_unlock(&ps3disk_mask_mutex); +- + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + error = -ENOMEM; +@@ -445,67 +457,85 @@ + goto fail_free_priv; + } + ++ for (regidx = 0; regidx < dev->num_regions; regidx++) ++ dev->regions[regidx].flags = region_flags[regidx]; ++ + error = ps3stor_setup(dev, ps3disk_interrupt); + if (error) + goto fail_free_bounce; + + ps3disk_identify(dev); + +- queue = blk_init_queue(ps3disk_request, &priv->lock); +- if (!queue) { +- dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", +- __func__, __LINE__); +- error = -ENOMEM; +- goto fail_teardown; +- } ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (test_bit(devidx, &dev->accessible_regions) == 0) ++ continue; + +- priv->queue = queue; +- queue->queuedata = dev; ++ queue = blk_init_queue(ps3disk_request, &priv->lock); ++ if (!queue) { ++ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", ++ __func__, __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } + +- blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); ++ priv->queue[devidx] = queue; ++ queue->queuedata = dev; + +- blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9); +- blk_queue_segment_boundary(queue, -1UL); +- blk_queue_dma_alignment(queue, dev->blk_size-1); +- blk_queue_logical_block_size(queue, dev->blk_size); ++ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); + +- blk_queue_flush(queue, REQ_FLUSH); ++ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9); ++ blk_queue_segment_boundary(queue, -1UL); ++ blk_queue_dma_alignment(queue, dev->blk_size-1); ++ blk_queue_logical_block_size(queue, dev->blk_size); ++ ++ blk_queue_flush(queue, REQ_FLUSH); ++ ++ blk_queue_max_segments(queue, -1); ++ blk_queue_max_segment_size(queue, dev->bounce_size); ++ ++ gendisk = alloc_disk(PS3DISK_MINORS); ++ if (!gendisk) { ++ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, ++ __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } + +- blk_queue_max_segments(queue, -1); +- blk_queue_max_segment_size(queue, dev->bounce_size); ++ priv->gendisk[devidx] = gendisk; ++ gendisk->major = ps3disk_major; ++ gendisk->first_minor = devidx * PS3DISK_MINORS; ++ gendisk->fops = &ps3disk_fops; ++ gendisk->queue = queue; ++ gendisk->private_data = dev; ++ gendisk->driverfs_dev = &dev->sbd.core; ++ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME, ++ devidx+'a'); ++ priv->blocking_factor = dev->blk_size >> 9; ++ set_capacity(gendisk, ++ dev->regions[devidx].size*priv->blocking_factor); ++ ++ dev_info(&dev->sbd.core, ++ "%s is a %s (%llu MiB total, %lu MiB region)\n", ++ gendisk->disk_name, priv->model, priv->raw_capacity >> 11, ++ get_capacity(gendisk) >> 11); + +- gendisk = alloc_disk(PS3DISK_MINORS); +- if (!gendisk) { +- dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, +- __LINE__); +- error = -ENOMEM; +- goto fail_cleanup_queue; ++ add_disk(gendisk); + } + +- priv->gendisk = gendisk; +- gendisk->major = ps3disk_major; +- gendisk->first_minor = devidx * PS3DISK_MINORS; +- gendisk->fops = &ps3disk_fops; +- gendisk->queue = queue; +- gendisk->private_data = dev; +- gendisk->driverfs_dev = &dev->sbd.core; +- snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME, +- devidx+'a'); +- priv->blocking_factor = dev->blk_size >> 9; +- set_capacity(gendisk, +- dev->regions[dev->region_idx].size*priv->blocking_factor); +- +- dev_info(&dev->sbd.core, +- "%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n", +- gendisk->disk_name, priv->model, priv->raw_capacity >> 11, +- get_capacity(gendisk) >> 11); +- +- add_disk(gendisk); + return 0; + +-fail_cleanup_queue: +- blk_cleanup_queue(queue); +-fail_teardown: ++fail_cleanup: ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } + ps3stor_teardown(dev); + fail_free_bounce: + kfree(dev->bounce_buf); +@@ -513,9 +543,6 @@ + kfree(priv); + ps3_system_bus_set_drvdata(_dev, NULL); + fail: +- mutex_lock(&ps3disk_mask_mutex); +- __clear_bit(devidx, &ps3disk_mask); +- mutex_unlock(&ps3disk_mask_mutex); + return error; + } + +@@ -523,14 +550,19 @@ + { + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ unsigned int devidx; ++ ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } + +- mutex_lock(&ps3disk_mask_mutex); +- __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS, +- &ps3disk_mask); +- mutex_unlock(&ps3disk_mask_mutex); +- del_gendisk(priv->gendisk); +- blk_cleanup_queue(priv->queue); +- put_disk(priv->gendisk); + dev_notice(&dev->sbd.core, "Synchronizing disk cache\n"); + ps3disk_sync_cache(dev); + ps3stor_teardown(dev); +--- a/drivers/scsi/ps3rom.c 2012-01-03 19:41:27.000000000 +0100 ++++ b/drivers/scsi/ps3rom.c 2012-01-05 16:38:58.103891705 +0100 +@@ -72,6 +72,14 @@ + }; + + ++static unsigned int region_flags[] = ++{ ++ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ++}; ++module_param_array(region_flags, uint, NULL, S_IRUGO); ++MODULE_PARM_DESC(region_flags, "Region flags"); ++ ++ + static int ps3rom_slave_configure(struct scsi_device *scsi_dev) + { + struct ps3rom_private *priv = shost_priv(scsi_dev->host); +@@ -172,12 +180,13 @@ + u32 sectors) + { + int res; ++ unsigned int region_idx = 0; + + dev_dbg(&dev->sbd.core, "%s:%u: read %u sectors starting at %u\n", + __func__, __LINE__, sectors, start_sector); + + res = lv1_storage_read(dev->sbd.dev_id, +- dev->regions[dev->region_idx].id, start_sector, ++ dev->regions[region_idx].id, start_sector, + sectors, 0, dev->bounce_lpar, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__, +@@ -193,6 +202,7 @@ + u32 sectors) + { + int res; ++ unsigned int region_idx = 0; + + dev_dbg(&dev->sbd.core, "%s:%u: write %u sectors starting at %u\n", + __func__, __LINE__, sectors, start_sector); +@@ -200,7 +210,7 @@ + scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size); + + res = lv1_storage_write(dev->sbd.dev_id, +- dev->regions[dev->region_idx].id, start_sector, ++ dev->regions[region_idx].id, start_sector, + sectors, 0, dev->bounce_lpar, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__, +@@ -362,6 +372,7 @@ + { + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + int error; ++ unsigned int regidx; + struct Scsi_Host *host; + struct ps3rom_private *priv; + +@@ -377,6 +388,9 @@ + if (!dev->bounce_buf) + return -ENOMEM; + ++ for (regidx = 0; regidx < dev->num_regions; regidx++) ++ dev->regions[regidx].flags = region_flags[regidx]; ++ + error = ps3stor_setup(dev, ps3rom_interrupt); + if (error) + goto fail_free_bounce; diff --git a/0020-ps3fb-use-fifo.patch b/0020-ps3fb-use-fifo.patch new file mode 100644 index 0000000..16f7855 --- /dev/null +++ b/0020-ps3fb-use-fifo.patch @@ -0,0 +1,537 @@ +--- a/arch/powerpc/include/asm/ps3gpu.h 2012-01-03 19:41:27.000000000 +0100 ++++ b/arch/powerpc/include/asm/ps3gpu.h 2012-01-05 23:17:51.200679863 +0100 +@@ -25,6 +25,10 @@ + #include + + ++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_SETUP 0x1 ++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_PAUSE 0x2 ++#define L1GPU_CONTEXT_ATTRIBUTE_FIFO_RESUME 0x3 ++ + #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101 + #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102 + +@@ -44,11 +48,11 @@ + + + static inline int lv1_gpu_display_sync(u64 context_handle, u64 head, +- u64 ddr_offset) ++ u64 sync_mode) + { + return lv1_gpu_context_attribute(context_handle, + L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, +- head, ddr_offset, 0, 0); ++ head, sync_mode, 0, 0); + } + + static inline int lv1_gpu_display_flip(u64 context_handle, u64 head, +--- a/drivers/video/ps3fb.c 2012-01-03 19:41:27.000000000 +0100 ++++ b/drivers/video/ps3fb.c 2012-01-05 23:17:32.550387632 +0100 +@@ -50,6 +50,7 @@ + + #define GPU_INTR_STATUS_VSYNC_0 0 /* vsync on head A */ + #define GPU_INTR_STATUS_VSYNC_1 1 /* vsync on head B */ ++#define GPU_INTR_STATUS_GRAPH_EXCEPTION 2 /* graphics exception */ + #define GPU_INTR_STATUS_FLIP_0 3 /* flip head A */ + #define GPU_INTR_STATUS_FLIP_1 4 /* flip head B */ + #define GPU_INTR_STATUS_QUEUE_0 5 /* queue head A */ +@@ -75,6 +76,22 @@ + u32 reserved2; + }; + ++struct gpu_graph_exception_info { ++ u32 channel_id; ++ u32 cause; ++ u32 res1[8]; ++ u32 dma_put; ++ u32 dma_get; ++ u32 call; ++ u32 jump; ++ u32 res2; ++ u32 fifo_put; ++ u32 fifo_get; ++ u32 fifo_ref; ++ u32 fifo_cache[512]; ++ u32 graph_fifo[512]; ++}; ++ + struct gpu_irq { + u32 irq_outlet; + u32 status; +@@ -82,11 +99,8 @@ + u32 video_cause; + u32 graph_cause; + u32 user_cause; +- +- u32 res1; +- u64 res2; +- +- u32 reserved[4]; ++ u32 res[8]; ++ struct gpu_graph_exception_info graph_exception_info; + }; + + struct gpu_driver_info { +@@ -103,11 +117,27 @@ + struct gpu_irq irq; + }; + ++struct gpu_fifo_ctrl { ++ u8 res[64]; ++ u32 put; ++ u32 get; ++ u32 ref; ++}; ++ ++struct gpu_fifo { ++ volatile struct gpu_fifo_ctrl *ctrl; ++ u32 *start; ++ u32 *curr; ++ unsigned int len; ++ u32 ioif; ++}; ++ + struct ps3fb_priv { + unsigned int irq_no; + + u64 context_handle, memory_handle; + struct gpu_driver_info *dinfo; ++ struct gpu_fifo fifo; + + u64 vblank_count; /* frame count */ + wait_queue_head_t wait_vsync; +@@ -260,8 +290,267 @@ + static int ps3fb_mode; + module_param(ps3fb_mode, int, 0); + ++static unsigned long ps3fb_gpu_ctx_flags = 0x820; ++module_param(ps3fb_gpu_ctx_flags, ulong, 0); ++ ++static unsigned long ps3fb_gpu_mem_size[4]; ++static int ps3fb_gpu_mem_size_count; ++module_param_array(ps3fb_gpu_mem_size, ulong, &ps3fb_gpu_mem_size_count, 0); ++ + static char *mode_option; + ++static int ps3fb_fb_setup(struct device *dev, ++ u64 context_handle, ++ struct gpu_fifo *fifo) ++{ ++ /* FIFO program for L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP from LV1 */ ++ const static u32 fifo_setup_program[] = { ++ 0x00042000, ++ 0x31337303, ++ ++ 0x00042180, ++ 0x66604200, ++ ++ 0x00082184, ++ 0xFEED0001, ++ 0xFEED0000, ++ ++ 0x00044000, ++ 0x3137C0DE, ++ ++ 0x00044180, ++ 0x66604200, ++ ++ 0x00084184, ++ 0xFEED0000, ++ 0xFEED0001, ++ ++ 0x00046000, ++ 0x313371C3, ++ ++ 0x00046180, ++ 0x66604200, ++ ++ 0x00046184, ++ 0xFEED0000, ++ ++ 0x00046188, ++ 0xFEED0000, ++ ++ 0x0004A000, ++ 0x31337808, ++ ++ 0x0020A180, ++ 0x66604200, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x00000000, ++ 0x313371C3, ++ ++ 0x0008A2FC, ++ 0x00000003, ++ 0x00000004, ++ ++ 0x00048000, ++ 0x31337A73, ++ ++ 0x00048180, ++ 0x66604200, ++ ++ 0x00048184, ++ 0xFEED0000, ++ ++ 0x0004C000, ++ 0x3137AF00, ++ ++ 0x0004C180, ++ 0x66604200, ++ ++ 0x00000000, ++ }; ++ int retval = 0; ++ int status; ++ volatile struct gpu_fifo_ctrl *fifo_ctrl = fifo->ctrl; ++ u32 *fifo_prev = fifo->curr; ++ unsigned int timeout; ++ ++ pr_debug("%s: enter\n", __func__); ++ ++ /* copy setup program to FIFO */ ++ memcpy(fifo->curr, fifo_setup_program, sizeof(fifo_setup_program)); ++ fifo->curr += sizeof(fifo_setup_program) / sizeof(u32); ++ ++ /* set PUT and GET registers */ ++ status = lv1_gpu_context_attribute(context_handle, ++ L1GPU_CONTEXT_ATTRIBUTE_FIFO_SETUP, ++ fifo->ioif, /* PUT */ ++ fifo->ioif, /* GET */ ++ 0, /* REF */ ++ 0); ++ if (status) { ++ dev_err(dev, "%s: lv1_gpu_context_attribute failed (%d)\n", ++ __func__, status); ++ retval = -ENXIO; ++ goto done; ++ } ++ ++ /* kick FIFO */ ++ fifo_ctrl->put += (fifo->curr - fifo_prev) * sizeof(u32); ++ ++ /* wait until FIFO is done */ ++ timeout = 100000; ++ while (timeout--) { ++ if (fifo_ctrl->put == fifo_ctrl->get) ++ break; ++ } ++ ++ if (fifo_ctrl->put != fifo_ctrl->get) ++ retval = -ETIMEDOUT; ++ ++done: ++ ++ pr_debug("%s: leave (%d)\n", __func__, retval); ++ ++ return (retval); ++} ++ ++static int ps3fb_fb_blit(struct gpu_fifo *fifo, ++ u64 dst_offset, u64 src_offset, ++ u32 width, u32 height, ++ u32 dst_pitch, u32 src_pitch, ++ u64 flags) ++{ ++#define BLEN 0x400UL ++ ++ int retval = 0; ++ volatile struct gpu_fifo_ctrl *fifo_ctrl = fifo->ctrl; ++ u32 *fifo_prev = fifo->curr; ++ unsigned int timeout; ++ u32 h, w, x, y, dx, dy; ++ ++ pr_debug("%s: enter\n", __func__); ++ ++ /* check if there is enough free space in FIFO */ ++ if ((fifo->len - ((fifo->curr - fifo->start) * sizeof(u32))) < 0x1000) { ++ /* no, jump back to FIFO start */ ++ ++ pr_debug("%s: not enough free space left in FIFO put (0x%08x) get (0x%08x)\n", ++ __func__, fifo_ctrl->put, fifo_ctrl->get); ++ ++ *fifo->curr++ = 0x20000000 /* JMP */ | fifo->ioif; ++ ++ /* kick FIFO */ ++ fifo_ctrl->put = fifo->ioif; ++ ++ /* wait until FIFO is done */ ++ timeout = 100000; ++ while (timeout--) { ++ if (fifo_ctrl->put == fifo_ctrl->get) ++ break; ++ } ++ ++ if (fifo_ctrl->put != fifo_ctrl->get) { ++ retval = -ETIMEDOUT; ++ goto done; ++ } ++ ++ fifo->curr = fifo->start; ++ fifo_prev = fifo->curr; ++ } ++ ++ /* FIFO program for L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT from LV1 (transfer image) */ ++ ++ /* set source location */ ++ *fifo->curr++ = 0x0004C184; ++ *fifo->curr++ = 0xFEED0001; /* GART memory */ ++ ++ *fifo->curr++ = 0x0004C198; ++ *fifo->curr++ = 0x313371C3; ++ ++ *fifo->curr++ = 0x00046300; ++ *fifo->curr++ = 0x0000000A; /* 4 bytes per pixel */ ++ ++ /* ++ * Transfer data in a block-wise fashion with block size 1024x1024x4 bytes ++ * by using RSX DMA controller. Go from top to bottom and from left to right. ++ */ ++ ++ h = height; ++ y = 0; ++ ++ while (h) { ++ dy = (h <= BLEN) ? h : BLEN; ++ ++ w = width; ++ x = 0; ++ ++ while (w) { ++ dx = (w <= BLEN) ? w : BLEN; ++ ++ *fifo->curr++ = 0x0004630C; ++ *fifo->curr++ = dst_offset + (y & ~(BLEN - 1)) * dst_pitch + (x & ~(BLEN - 1)) * BPP; /* destination */ ++ ++ *fifo->curr++ = 0x00046304; ++ *fifo->curr++ = (dst_pitch << 16) | dst_pitch; ++ ++ *fifo->curr++ = 0x0024C2FC; ++ *fifo->curr++ = 0x00000001; ++ *fifo->curr++ = 0x00000003; /* 4 bytes per pixel */ ++ *fifo->curr++ = 0x00000003; ++ *fifo->curr++ = ((x & (BLEN - 1)) << 16) | (y & (BLEN - 1)); ++ *fifo->curr++ = (dy << 16) | dx; ++ *fifo->curr++ = ((x & (BLEN - 1)) << 16) | (y & (BLEN - 1)); ++ *fifo->curr++ = (dy << 16) | dx; ++ *fifo->curr++ = 0x00100000; ++ *fifo->curr++ = 0x00100000; ++ ++ *fifo->curr++ = 0x0010C400; ++ *fifo->curr++ = (dy << 16) | ((dx < 0x10) ? 0x10 : (dx + 1) & ~0x1); ++ *fifo->curr++ = 0x00020000 | src_pitch; ++ *fifo->curr++ = src_offset + y * src_pitch + x * BPP; /* source */ ++ *fifo->curr++ = 0x00000000; ++ ++ w -= dx; ++ x += dx; ++ } ++ ++ h -= dy; ++ y += dy; ++ } ++ ++#if 0 ++ /* wait for idle */ ++ *fifo->curr++ = 0x00040110; ++ *fifo->curr++ = 0x00000000; ++#endif ++ ++ /* kick FIFO */ ++ fifo_ctrl->put += (fifo->curr - fifo_prev) * sizeof(u32); ++ ++ /* wait until FIFO is done */ ++ if (flags & L1GPU_FB_BLIT_WAIT_FOR_COMPLETION) { ++ timeout = 100000; ++ while (timeout--) { ++ if (fifo_ctrl->put == fifo_ctrl->get) ++ break; ++ } ++ ++ if (fifo_ctrl->put != fifo_ctrl->get) ++ retval = -ETIMEDOUT; ++ } ++ ++done: ++ ++ pr_debug("%s: leave (%d)\n", __func__, retval); ++ ++ return (retval); ++ ++#undef BLEN ++} ++ + static int ps3fb_cmp_mode(const struct fb_videomode *vmode, + const struct fb_var_screeninfo *var) + { +@@ -444,24 +733,20 @@ + u32 src_line_length) + { + int status; +- u64 line_length; +- +- line_length = dst_line_length; +- if (src_line_length != dst_line_length) +- line_length |= (u64)src_line_length << 32; + + src_offset += GPU_FB_START; + + mutex_lock(&ps3_gpu_mutex); +- status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset, +- GPU_IOIF + src_offset, +- L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | +- (width << 16) | height, +- line_length); ++ status = ps3fb_fb_blit(&ps3fb.fifo, ++ dst_offset, ++ GPU_IOIF + src_offset, ++ width, height, ++ dst_line_length, src_line_length, ++ L1GPU_FB_BLIT_WAIT_FOR_COMPLETION); + mutex_unlock(&ps3_gpu_mutex); + + if (status) +- dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__, ++ dev_err(dev, "%s: ps3fb_fb_blit failed: %d\n", __func__, + status); + #ifdef HEAD_A + status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset); +@@ -912,6 +1197,34 @@ + return 0; + } + ++static void ps3fb_print_graph_exception_info(struct device *dev, ++ struct gpu_graph_exception_info *info) ++{ ++ int i; ++ ++ dev_err(dev, "channel id 0x%08x cause 0x%08x\n", info->channel_id, info->cause); ++ ++ /* print FIFO info */ ++ ++ dev_err(dev, "fifo:\n"); ++ dev_err(dev, "\tdma get 0x%08x dma put 0x%08x\n", ++ info->dma_get, info->dma_put); ++ dev_err(dev, "\tcall 0x%08x jump 0x%08x\n", info->call, info->jump); ++ dev_err(dev, "\tget 0x%08x put 0x%08x ref 0x%08x\n", ++ info->fifo_get, info->fifo_put, info->fifo_ref); ++ ++ for (i = 0; i < 512; i += 4) { ++ dev_err(dev, "\t%s %s [%03x] %08x:%08x %08x:%08x %08x:%08x %08x:%08x\n", ++ (((info->fifo_put & ~0x3) == i) ? "P" : " "), ++ (((info->fifo_get & ~0x3) == i) ? "G" : " "), ++ i, ++ info->fifo_cache[i * 4 + 0], info->graph_fifo[i * 4 + 0], ++ info->fifo_cache[i * 4 + 1], info->graph_fifo[i * 4 + 1], ++ info->fifo_cache[i * 4 + 2], info->graph_fifo[i * 4 + 2], ++ info->fifo_cache[i * 4 + 3], info->graph_fifo[i * 4 + 3]); ++ } ++} ++ + static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) + { + struct device *dev = ptr; +@@ -926,6 +1239,11 @@ + return IRQ_NONE; + } + ++ if (v1 & (1 << GPU_INTR_STATUS_GRAPH_EXCEPTION)) { ++ dev_err(dev, "%s: graphics exception\n", __func__); ++ ps3fb_print_graph_exception_info(dev, &ps3fb.dinfo->irq.graph_exception_info); ++ } ++ + if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) { + /* VSYNC */ + ps3fb.vblank_count = head->vblank_count; +@@ -1030,8 +1348,9 @@ + } + + /* get gpu context handle */ +- status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0, +- &ps3fb.memory_handle, &ddr_lpar); ++ status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, ps3fb_gpu_mem_size[0], ++ ps3fb_gpu_mem_size[1], ps3fb_gpu_mem_size[2], ps3fb_gpu_mem_size[3], ++ &ps3fb.memory_handle, &ddr_lpar); + if (status) { + dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n", + __func__, status); +@@ -1039,7 +1358,7 @@ + } + dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar); + +- status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0, ++ status = lv1_gpu_context_allocate(ps3fb.memory_handle, ps3fb_gpu_ctx_flags, + &ps3fb.context_handle, + &lpar_dma_control, &lpar_driver_info, + &lpar_reports, &lpar_reports_size); +@@ -1089,7 +1408,8 @@ + goto err_destroy_plug; + } + +- dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) | ++ dinfo->irq.mask = (1 << GPU_INTR_STATUS_GRAPH_EXCEPTION) | ++ (1 << GPU_INTR_STATUS_VSYNC_1) | + (1 << GPU_INTR_STATUS_FLIP_1); + + /* Clear memory to prevent kernel info leakage into userspace */ +@@ -1434,19 +1434,30 @@ + ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size); + +- status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar, +- GPU_CMD_BUF_SIZE, GPU_IOIF); ++ /* FIFO control */ ++ ps3fb.fifo.ctrl = (void __force *)ioremap_nocache(lpar_dma_control, PAGE_SIZE); ++ if (!ps3fb.fifo.ctrl) { ++ dev_err(&dev->core, "%s: ioremap_nocache failed\n", __func__); ++ goto err_context_unmap; ++ } ++ ++ ps3fb.fifo.start = ps3fb_videomemory.address; ++ ps3fb.fifo.curr = ps3fb.fifo.start; ++ ps3fb.fifo.len = GPU_FB_START; ++ ps3fb.fifo.ioif = GPU_IOIF; ++ ++ status = ps3fb_fb_setup(&dev->core, ps3fb.context_handle, &ps3fb.fifo); + if (status) { +- dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n", ++ dev_err(&dev->core, "%s: ps3fb_fb_setup failed: %d\n", + __func__, status); +- retval = -ENXIO; +- goto err_context_unmap; ++ retval = status; ++ goto err_iounmap_fifo_ctrl; + } + + info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core); + if (!info) { + retval = -ENOMEM; +- goto err_context_fb_close; ++ goto err_iounmap_fifo_ctrl; + } + + par = info->par; +@@ -1188,8 +1519,8 @@ + fb_dealloc_cmap(&info->cmap); + err_framebuffer_release: + framebuffer_release(info); +-err_context_fb_close: +- lv1_gpu_fb_close(ps3fb.context_handle); ++err_iounmap_fifo_ctrl: ++ iounmap((u8 __force __iomem *)ps3fb.fifo.ctrl); + err_context_unmap: + lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size, CBE_IOPTE_M); +@@ -1235,7 +1566,7 @@ + ps3_system_bus_set_drvdata(dev, NULL); + } + iounmap((u8 __force __iomem *)ps3fb.dinfo); +- lv1_gpu_fb_close(ps3fb.context_handle); ++ iounmap((u8 __force __iomem *)ps3fb.fifo.ctrl); + lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, + ps3fb_videomemory.size, CBE_IOPTE_M); + lv1_gpu_context_free(ps3fb.context_handle); diff --git a/0030-ps3flash.patch b/0030-ps3flash.patch new file mode 100644 index 0000000..81eee59 --- /dev/null +++ b/0030-ps3flash.patch @@ -0,0 +1,1076 @@ +--- a/arch/powerpc/include/asm/ps3.h 2012-01-24 18:09:05.452290538 +0100 ++++ b/arch/powerpc/include/asm/ps3.h 2012-01-24 18:37:24.181894425 +0100 +@@ -326,6 +326,7 @@ + PS3_MATCH_ID_SOUND = 9, + PS3_MATCH_ID_GPU = 10, + PS3_MATCH_ID_LPM = 11, ++ PS3_MATCH_ID_STOR_NOR_FLASH = 12, + }; + + enum ps3_match_sub_id { +@@ -345,6 +346,7 @@ + #define PS3_MODULE_ALIAS_GPU_FB "ps3:10:1" + #define PS3_MODULE_ALIAS_GPU_RAMDISK "ps3:10:2" + #define PS3_MODULE_ALIAS_LPM "ps3:11:0" ++#define PS3_MODULE_ALIAS_STOR_NOR_FLASH "ps3:12:0" + + enum ps3_system_bus_device_type { + PS3_DEVICE_TYPE_IOC0 = 1, +--- a/arch/powerpc/platforms/ps3/platform.h 2012-01-12 20:42:45.000000000 +0100 ++++ b/arch/powerpc/platforms/ps3/platform.h 2012-01-24 18:36:02.470631575 +0100 +@@ -88,6 +88,7 @@ + PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ + PS3_DEV_TYPE_SB_GPIO = 6, + PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ ++ PS3_DEV_TYPE_STOR_NOR_FLASH = 254, + }; + + int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, +--- a/arch/powerpc/platforms/ps3/device-init.c 2012-01-12 20:42:45.000000000 +0100 ++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-01-24 18:34:44.089470755 +0100 +@@ -592,6 +592,13 @@ + __func__, __LINE__); + break; + ++ case PS3_DEV_TYPE_STOR_NOR_FLASH: ++ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_NOR_FLASH); ++ if (result) ++ pr_debug("%s:%u ps3_setup_storage_dev failed\n", ++ __func__, __LINE__); ++ break; ++ + default: + result = 0; + pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, +--- a/arch/powerpc/platforms/ps3/system-bus.c 2012-01-12 20:42:45.000000000 +0100 ++++ b/arch/powerpc/platforms/ps3/system-bus.c 2012-01-24 19:25:53.073547395 +0100 +@@ -174,6 +174,7 @@ + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: ++ case PS3_MATCH_ID_STOR_NOR_FLASH: + return ps3_open_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: +@@ -212,6 +213,7 @@ + case PS3_MATCH_ID_STOR_DISK: + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: ++ case PS3_MATCH_ID_STOR_NOR_FLASH: + return ps3_close_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-01-24 19:02:19.221491270 +0100 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-01-24 19:03:21.419302797 +0100 +@@ -128,6 +128,26 @@ + be disabled on the kernel command line using "ps3flash=off", to + not allocate this fixed buffer. + ++config PS3_FLASH_NG ++ tristate "PS3 Flash Storage Driver" ++ depends on PPC_PS3 && BLOCK && PS3_FLASH!=y && PS3_FLASH!=m ++ select PS3_STORAGE ++ help ++ Include support for the PS3 Flash Storage. ++ ++ This support is required to access the PS3 flash. ++ In general, all users will say Y or M. ++ ++config PS3_NOR_FLASH ++ tristate "PS3 NOR Flash Storage Driver" ++ depends on PPC_PS3 && BLOCK ++ select PS3_STORAGE ++ help ++ Include support for the PS3 NOR Flash Storage. ++ ++ This support is required to access the PS3 NOR flash. ++ In general, all users will say Y or M. ++ + config PS3_VRAM + tristate "PS3 Video RAM Storage Driver" + depends on FB_PS3=y && BLOCK && m +--- a/drivers/block/Makefile 2012-01-24 19:02:39.908639282 +0100 ++++ b/drivers/block/Makefile 2012-01-24 18:32:56.294515362 +0100 +@@ -10,6 +10,8 @@ + obj-$(CONFIG_BLK_DEV_FD) += floppy.o + obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o + obj-$(CONFIG_PS3_DISK) += ps3disk.o ++obj-$(CONFIG_PS3_FLASH_NG) += ps3flash.o ++obj-$(CONFIG_PS3_NOR_FLASH) += ps3nflash.o + obj-$(CONFIG_PS3_VRAM) += ps3vram.o + obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o + obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o +--- /dev/null 2012-11-28 15:48:49.557690341 +0100 ++++ b/drivers/block/ps3flash.c 2013-02-14 11:18:29.076533279 +0100 +@@ -0,0 +1,520 @@ ++/* ++ * PS3 Flash Storage Driver ++ * ++ * Copyright (C) 2007 Sony Computer Entertainment Inc. ++ * Copyright 2007 Sony Corp. ++ * Copyright (C) 2011 graf_chokolo . ++ * Copyright (C) 2011-2013 glevand . ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++#define DEVICE_NAME "ps3flash" ++ ++#define BOUNCE_SIZE (64*1024) ++ ++#define PS3FLASH_MAX_NUM_REGS 8 ++ ++#define PS3FLASH_MINORS 16 ++ ++ ++#define PS3FLASH_NAME "ps3flash%c" ++ ++ ++struct ps3flash_private { ++ spinlock_t lock; /* Request queue spinlock */ ++ unsigned int blocking_factor; ++ struct request *req; ++ u64 raw_capacity; ++ int is_vflash; ++ struct gendisk *gendisk[PS3FLASH_MAX_NUM_REGS]; ++ struct request_queue *queue[PS3FLASH_MAX_NUM_REGS]; ++ int next_queue; ++}; ++ ++ ++#define LV1_STORAGE_ATA_FLUSH_CACHE_EXT (0x31) ++ ++ ++static int ps3flash_major; ++ ++ ++static const struct block_device_operations ps3flash_fops = { ++ .owner = THIS_MODULE, ++}; ++ ++static unsigned int region_flags[] = ++{ ++ 0x6, 0x2, 0x4, 0x4, 0x4, 0x0, 0x2, 0x0, ++}; ++module_param_array(region_flags, uint, NULL, S_IRUGO); ++MODULE_PARM_DESC(region_flags, "Region flags"); ++ ++ ++static void ps3flash_scatter_gather(struct ps3_storage_device *dev, ++ struct request *req, int gather) ++{ ++ unsigned int offset = 0; ++ struct req_iterator iter; ++ struct bio_vec *bvec; ++ unsigned int i = 0; ++ size_t size; ++ void *buf; ++ ++ rq_for_each_segment(bvec, req, iter) { ++ unsigned long flags; ++ dev_dbg(&dev->sbd.core, ++ "%s:%u: bio %u: %u segs %u sectors from %lu\n", ++ __func__, __LINE__, i, bio_segments(iter.bio), ++ bio_sectors(iter.bio), iter.bio->bi_sector); ++ ++ size = bvec->bv_len; ++ buf = bvec_kmap_irq(bvec, &flags); ++ if (gather) ++ memcpy(dev->bounce_buf+offset, buf, size); ++ else ++ memcpy(buf, dev->bounce_buf+offset, size); ++ offset += size; ++ flush_kernel_dcache_page(bvec->bv_page); ++ bvec_kunmap_irq(buf, &flags); ++ i++; ++ } ++} ++ ++static int ps3flash_submit_request_sg(struct ps3_storage_device *dev, ++ struct request *req) ++{ ++ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ int write = rq_data_dir(req), res; ++ const char *op = write ? "write" : "read"; ++ u64 start_sector, sectors; ++ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3FLASH_MINORS; ++ unsigned int region_id = dev->regions[region_idx].id; ++ unsigned int region_flags = dev->regions[region_idx].flags; ++ ++#ifdef DEBUG ++ unsigned int n = 0; ++ struct bio_vec *bv; ++ struct req_iterator iter; ++ ++ rq_for_each_segment(bv, req, iter) ++ n++; ++ dev_dbg(&dev->sbd.core, ++ "%s:%u: %s req has %u bvecs for %u sectors\n", ++ __func__, __LINE__, op, n, blk_rq_sectors(req)); ++#endif ++ ++ start_sector = blk_rq_pos(req) * priv->blocking_factor; ++ sectors = blk_rq_sectors(req) * priv->blocking_factor; ++ dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", ++ __func__, __LINE__, op, sectors, start_sector); ++ ++ if (write) { ++ ps3flash_scatter_gather(dev, req, 1); ++ ++ res = lv1_storage_write(dev->sbd.dev_id, region_id, ++ start_sector, sectors, region_flags, ++ dev->bounce_lpar, &dev->tag); ++ } else { ++ res = lv1_storage_read(dev->sbd.dev_id, region_id, ++ start_sector, sectors, region_flags, ++ dev->bounce_lpar, &dev->tag); ++ } ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, ++ __LINE__, op, res); ++ __blk_end_request_all(req, -EIO); ++ return 0; ++ } ++ ++ priv->req = req; ++ return 1; ++} ++ ++static int ps3flash_submit_flush_request(struct ps3_storage_device *dev, ++ struct request *req) ++{ ++ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ u64 res; ++ ++ dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__); ++ ++ res = lv1_storage_send_device_command(dev->sbd.dev_id, ++ LV1_STORAGE_ATA_FLUSH_CACHE_EXT, 0, 0, 0, ++ 0, &dev->tag); ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n", ++ __func__, __LINE__, res); ++ __blk_end_request_all(req, -EIO); ++ return 0; ++ } ++ ++ priv->req = req; ++ return 1; ++} ++ ++static void ps3flash_do_request(struct ps3_storage_device *dev, ++ struct request_queue *q) ++{ ++ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ struct request *req; ++ ++ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); ++ ++ while ((req = blk_fetch_request(q))) { ++ if (priv->is_vflash && (req->cmd_flags & REQ_FLUSH)) { ++ if (ps3flash_submit_flush_request(dev, req)) ++ break; ++ } else if (req->cmd_type == REQ_TYPE_FS) { ++ if (ps3flash_submit_request_sg(dev, req)) ++ break; ++ } else { ++ blk_dump_rq_flags(req, DEVICE_NAME " bad request"); ++ __blk_end_request_all(req, -EIO); ++ continue; ++ } ++ } ++} ++ ++static void ps3flash_request(struct request_queue *q) ++{ ++ struct ps3_storage_device *dev = q->queuedata; ++ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ ++ if (priv->req) { ++ dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__); ++ return; ++ } ++ ++ ps3flash_do_request(dev, q); ++} ++ ++static irqreturn_t ps3flash_interrupt(int irq, void *data) ++{ ++ struct ps3_storage_device *dev = data; ++ struct ps3flash_private *priv; ++ struct request *req; ++ int res, read, error; ++ u64 tag, status; ++ const char *op; ++ struct request_queue *q; ++ int old_queue; ++ ++ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); ++ ++ if (tag != dev->tag) ++ dev_err(&dev->sbd.core, ++ "%s:%u: tag mismatch, got %llx, expected %llx\n", ++ __func__, __LINE__, tag, dev->tag); ++ ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n", ++ __func__, __LINE__, res, status); ++ return IRQ_HANDLED; ++ } ++ ++ priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ req = priv->req; ++ if (!req) { ++ dev_dbg(&dev->sbd.core, ++ "%s:%u non-block layer request completed\n", __func__, ++ __LINE__); ++ dev->lv1_status = status; ++ complete(&dev->done); ++ return IRQ_HANDLED; ++ } ++ ++ if (req->cmd_flags & REQ_FLUSH) { ++ read = 0; ++ op = "flush"; ++ } else { ++ read = !rq_data_dir(req); ++ op = read ? "read" : "write"; ++ } ++ if (status) { ++ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, ++ __LINE__, op, status); ++ error = -EIO; ++ } else { ++ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, ++ __LINE__, op); ++ error = 0; ++ if (read) ++ ps3flash_scatter_gather(dev, req, 0); ++ } ++ ++ spin_lock(&priv->lock); ++ __blk_end_request_all(req, error); ++ priv->req = NULL; ++ old_queue = priv->next_queue; ++ do { ++ q = priv->queue[priv->next_queue]; ++ ++ priv->next_queue++; ++ if (priv->next_queue >= dev->num_regions) ++ priv->next_queue = 0; ++ ++ if (q) { ++ ps3flash_do_request(dev, q); ++ if (priv->req) ++ break; ++ } ++ } while (old_queue != priv->next_queue); ++ spin_unlock(&priv->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ps3flash_sync_cache(struct ps3_storage_device *dev) ++{ ++ u64 res; ++ ++ dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__); ++ ++ res = ps3stor_send_command(dev, LV1_STORAGE_ATA_FLUSH_CACHE_EXT, 0, 0, 0, 0); ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n", ++ __func__, __LINE__, res); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int ps3flash_probe(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3flash_private *priv; ++ int error; ++ unsigned int regidx, devidx; ++ u64 lpar_id, flash_ext_flag, junk; ++ struct request_queue *queue; ++ struct gendisk *gendisk; ++ ++ BUG_ON(dev->num_regions > PS3FLASH_MAX_NUM_REGS); ++ ++ if (dev->blk_size < 512) { ++ dev_err(&dev->sbd.core, ++ "%s:%u: cannot handle block size %llu\n", __func__, ++ __LINE__, dev->blk_size); ++ return -EINVAL; ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ error = -ENOMEM; ++ goto fail; ++ } ++ ++ ps3_system_bus_set_drvdata(_dev, priv); ++ spin_lock_init(&priv->lock); ++ ++ dev->bounce_size = BOUNCE_SIZE; ++ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); ++ if (!dev->bounce_buf) { ++ error = -ENOMEM; ++ goto fail_free_priv; ++ } ++ ++ for (regidx = 0; regidx < dev->num_regions; regidx++) ++ dev->regions[regidx].flags = region_flags[regidx]; ++ ++ error = ps3stor_setup(dev, ps3flash_interrupt); ++ if (error) ++ goto fail_free_bounce; ++ ++ priv->raw_capacity = dev->regions[0].size; ++ ++ error = lv1_get_logical_partition_id(&lpar_id); ++ if (error) ++ goto fail_teardown; ++ ++ error = lv1_read_repository_node(1, 0x0000000073797300ul /* sys */, ++ 0x666c617368000000ul /* flash */, ++ 0x6578740000000000ul /* ext */, ++ 0, &flash_ext_flag, &junk); ++ if (error) ++ goto fail_teardown; ++ ++ priv->is_vflash = !(flash_ext_flag & 0x1); ++ ++ dev_info(&dev->sbd.core, "VFLASH is %s\n", ++ priv->is_vflash ? "on" : "off"); ++ ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (test_bit(devidx, &dev->accessible_regions) == 0) ++ continue; ++ ++ queue = blk_init_queue(ps3flash_request, &priv->lock); ++ if (!queue) { ++ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", ++ __func__, __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } ++ ++ priv->queue[devidx] = queue; ++ queue->queuedata = dev; ++ ++ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); ++ ++ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9); ++ blk_queue_segment_boundary(queue, -1UL); ++ blk_queue_dma_alignment(queue, dev->blk_size-1); ++ blk_queue_logical_block_size(queue, dev->blk_size); ++ ++ blk_queue_flush(queue, REQ_FLUSH); ++ ++ blk_queue_max_segments(queue, -1); ++ blk_queue_max_segment_size(queue, dev->bounce_size); ++ ++ gendisk = alloc_disk(PS3FLASH_MINORS); ++ if (!gendisk) { ++ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, ++ __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } ++ ++ priv->gendisk[devidx] = gendisk; ++ gendisk->major = ps3flash_major; ++ gendisk->first_minor = devidx * PS3FLASH_MINORS; ++ gendisk->fops = &ps3flash_fops; ++ gendisk->queue = queue; ++ gendisk->private_data = dev; ++ gendisk->driverfs_dev = &dev->sbd.core; ++ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3FLASH_NAME, ++ devidx+'a'); ++ priv->blocking_factor = dev->blk_size >> 9; ++ set_capacity(gendisk, ++ dev->regions[devidx].size*priv->blocking_factor); ++ ++ dev_info(&dev->sbd.core, ++ "%s (%llu MiB total, %lu MiB region)\n", ++ gendisk->disk_name, priv->raw_capacity >> 11, ++ get_capacity(gendisk) >> 11); ++ ++ add_disk(gendisk); ++ } ++ ++ return 0; ++ ++fail_cleanup: ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } ++fail_teardown: ++ ps3stor_teardown(dev); ++fail_free_bounce: ++ kfree(dev->bounce_buf); ++fail_free_priv: ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++fail: ++ return error; ++} ++ ++static int ps3flash_remove(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3flash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ unsigned int devidx; ++ ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } ++ ++ if (priv->is_vflash) { ++ dev_notice(&dev->sbd.core, "Synchronizing disk cache\n"); ++ ps3flash_sync_cache(dev); ++ } ++ ++ ps3stor_teardown(dev); ++ kfree(dev->bounce_buf); ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++ return 0; ++} ++ ++static struct ps3_system_bus_driver ps3flash = { ++ .match_id = PS3_MATCH_ID_STOR_FLASH, ++ .core.name = DEVICE_NAME, ++ .core.owner = THIS_MODULE, ++ .probe = ps3flash_probe, ++ .remove = ps3flash_remove, ++ .shutdown = ps3flash_remove, ++}; ++ ++ ++static int __init ps3flash_init(void) ++{ ++ int error; ++ ++ if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) ++ return -ENODEV; ++ ++ error = register_blkdev(0, DEVICE_NAME); ++ if (error <= 0) { ++ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, ++ __LINE__, error); ++ return error; ++ } ++ ps3flash_major = error; ++ ++ pr_info("%s:%u: registered block device major %d\n", __func__, ++ __LINE__, ps3flash_major); ++ ++ error = ps3_system_bus_driver_register(&ps3flash); ++ if (error) ++ unregister_blkdev(ps3flash_major, DEVICE_NAME); ++ ++ return error; ++} ++ ++static void __exit ps3flash_exit(void) ++{ ++ ps3_system_bus_driver_unregister(&ps3flash); ++ unregister_blkdev(ps3flash_major, DEVICE_NAME); ++} ++ ++module_init(ps3flash_init); ++module_exit(ps3flash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("PS3 Flash Storage Driver"); ++MODULE_AUTHOR("Sony Corporation"); ++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH); +--- /dev/null 2013-02-15 18:37:48.373100583 +0100 ++++ b/drivers/block/ps3nflash.c 2013-02-15 23:15:44.710737431 +0100 +@@ -0,0 +1,448 @@ ++/* ++ * PS3 Flash Storage Driver ++ * ++ * Copyright (C) 2007 Sony Computer Entertainment Inc. ++ * Copyright 2007 Sony Corp. ++ * Copyright (C) 2011 graf_chokolo . ++ * Copyright (C) 2011-2013 glevand . ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++#define DEVICE_NAME "ps3nflash" ++ ++#define BOUNCE_SIZE (64*1024) ++ ++#define PS3NFLASH_MAX_NUM_REGS 8 ++ ++#define PS3NFLASH_MINORS 16 ++ ++ ++#define PS3NFLASH_NAME "ps3nflash%c" ++ ++ ++struct ps3nflash_private { ++ spinlock_t lock; /* Request queue spinlock */ ++ unsigned int blocking_factor; ++ struct request *req; ++ u64 raw_capacity; ++ struct gendisk *gendisk[PS3NFLASH_MAX_NUM_REGS]; ++ struct request_queue *queue[PS3NFLASH_MAX_NUM_REGS]; ++ int next_queue; ++}; ++ ++ ++static int ps3nflash_major; ++ ++ ++static const struct block_device_operations ps3nflash_fops = { ++ .owner = THIS_MODULE, ++}; ++ ++static unsigned int region_flags[] = ++{ ++ 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, ++}; ++module_param_array(region_flags, uint, NULL, S_IRUGO); ++MODULE_PARM_DESC(region_flags, "Region flags"); ++ ++ ++static void ps3nflash_scatter_gather(struct ps3_storage_device *dev, ++ struct request *req, int gather) ++{ ++ unsigned int offset = 0; ++ struct req_iterator iter; ++ struct bio_vec *bvec; ++ unsigned int i = 0; ++ size_t size; ++ void *buf; ++ ++ rq_for_each_segment(bvec, req, iter) { ++ unsigned long flags; ++ dev_dbg(&dev->sbd.core, ++ "%s:%u: bio %u: %u segs %u sectors from %lu\n", ++ __func__, __LINE__, i, bio_segments(iter.bio), ++ bio_sectors(iter.bio), iter.bio->bi_sector); ++ ++ size = bvec->bv_len; ++ buf = bvec_kmap_irq(bvec, &flags); ++ if (gather) ++ memcpy(dev->bounce_buf+offset, buf, size); ++ else ++ memcpy(buf, dev->bounce_buf+offset, size); ++ offset += size; ++ flush_kernel_dcache_page(bvec->bv_page); ++ bvec_kunmap_irq(buf, &flags); ++ i++; ++ } ++} ++ ++static int ps3nflash_submit_request_sg(struct ps3_storage_device *dev, ++ struct request *req) ++{ ++ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ int write = rq_data_dir(req), res; ++ const char *op = write ? "write" : "read"; ++ u64 start_sector, sectors; ++ unsigned int region_idx = MINOR(disk_devt(req->rq_disk)) / PS3NFLASH_MINORS; ++ unsigned int region_id = dev->regions[region_idx].id; ++ unsigned int region_flags = dev->regions[region_idx].flags; ++ ++#ifdef DEBUG ++ unsigned int n = 0; ++ struct bio_vec *bv; ++ struct req_iterator iter; ++ ++ rq_for_each_segment(bv, req, iter) ++ n++; ++ dev_dbg(&dev->sbd.core, ++ "%s:%u: %s req has %u bvecs for %u sectors\n", ++ __func__, __LINE__, op, n, blk_rq_sectors(req)); ++#endif ++ ++ start_sector = blk_rq_pos(req) * priv->blocking_factor; ++ sectors = blk_rq_sectors(req) * priv->blocking_factor; ++ dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", ++ __func__, __LINE__, op, sectors, start_sector); ++ ++ if (write) { ++ ps3nflash_scatter_gather(dev, req, 1); ++ ++ res = lv1_storage_write(dev->sbd.dev_id, region_id, ++ start_sector, sectors, region_flags, ++ dev->bounce_lpar, &dev->tag); ++ } else { ++ res = lv1_storage_read(dev->sbd.dev_id, region_id, ++ start_sector, sectors, region_flags, ++ dev->bounce_lpar, &dev->tag); ++ } ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, ++ __LINE__, op, res); ++ __blk_end_request_all(req, -EIO); ++ return 0; ++ } ++ ++ priv->req = req; ++ return 1; ++} ++ ++static void ps3nflash_do_request(struct ps3_storage_device *dev, ++ struct request_queue *q) ++{ ++ struct request *req; ++ ++ dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); ++ ++ while ((req = blk_fetch_request(q))) { ++ if (req->cmd_type == REQ_TYPE_FS) { ++ if (ps3nflash_submit_request_sg(dev, req)) ++ break; ++ } else { ++ blk_dump_rq_flags(req, DEVICE_NAME " bad request"); ++ __blk_end_request_all(req, -EIO); ++ continue; ++ } ++ } ++} ++ ++static void ps3nflash_request(struct request_queue *q) ++{ ++ struct ps3_storage_device *dev = q->queuedata; ++ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ ++ if (priv->req) { ++ dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__); ++ return; ++ } ++ ++ ps3nflash_do_request(dev, q); ++} ++ ++static irqreturn_t ps3nflash_interrupt(int irq, void *data) ++{ ++ struct ps3_storage_device *dev = data; ++ struct ps3nflash_private *priv; ++ struct request *req; ++ int res, read, error; ++ u64 tag, status; ++ const char *op; ++ struct request_queue *q; ++ int old_queue; ++ ++ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); ++ ++ if (tag != dev->tag) ++ dev_err(&dev->sbd.core, ++ "%s:%u: tag mismatch, got %llx, expected %llx\n", ++ __func__, __LINE__, tag, dev->tag); ++ ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n", ++ __func__, __LINE__, res, status); ++ return IRQ_HANDLED; ++ } ++ ++ priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ req = priv->req; ++ if (!req) { ++ dev_dbg(&dev->sbd.core, ++ "%s:%u non-block layer request completed\n", __func__, ++ __LINE__); ++ dev->lv1_status = status; ++ complete(&dev->done); ++ return IRQ_HANDLED; ++ } ++ ++ read = !rq_data_dir(req); ++ op = read ? "read" : "write"; ++ ++ if (status) { ++ dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, ++ __LINE__, op, status); ++ error = -EIO; ++ } else { ++ dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, ++ __LINE__, op); ++ error = 0; ++ if (read) ++ ps3nflash_scatter_gather(dev, req, 0); ++ } ++ ++ spin_lock(&priv->lock); ++ __blk_end_request_all(req, error); ++ priv->req = NULL; ++ old_queue = priv->next_queue; ++ do { ++ q = priv->queue[priv->next_queue]; ++ ++ priv->next_queue++; ++ if (priv->next_queue >= dev->num_regions) ++ priv->next_queue = 0; ++ ++ if (q) { ++ ps3nflash_do_request(dev, q); ++ if (priv->req) ++ break; ++ } ++ } while (old_queue != priv->next_queue); ++ spin_unlock(&priv->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ps3nflash_probe(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3nflash_private *priv; ++ int error; ++ unsigned int regidx, devidx; ++ struct request_queue *queue; ++ struct gendisk *gendisk; ++ ++ BUG_ON(dev->num_regions > PS3NFLASH_MAX_NUM_REGS); ++ ++ if (dev->blk_size < 512) { ++ dev_err(&dev->sbd.core, ++ "%s:%u: cannot handle block size %llu\n", __func__, ++ __LINE__, dev->blk_size); ++ return -EINVAL; ++ } ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ error = -ENOMEM; ++ goto fail; ++ } ++ ++ ps3_system_bus_set_drvdata(_dev, priv); ++ spin_lock_init(&priv->lock); ++ ++ dev->bounce_size = BOUNCE_SIZE; ++ dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); ++ if (!dev->bounce_buf) { ++ error = -ENOMEM; ++ goto fail_free_priv; ++ } ++ ++ for (regidx = 0; regidx < dev->num_regions; regidx++) ++ dev->regions[regidx].flags = region_flags[regidx]; ++ ++ error = ps3stor_setup(dev, ps3nflash_interrupt); ++ if (error) ++ goto fail_free_bounce; ++ ++ priv->raw_capacity = dev->regions[0].size; ++ ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (test_bit(devidx, &dev->accessible_regions) == 0) ++ continue; ++ ++ queue = blk_init_queue(ps3nflash_request, &priv->lock); ++ if (!queue) { ++ dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", ++ __func__, __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } ++ ++ priv->queue[devidx] = queue; ++ queue->queuedata = dev; ++ ++ blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); ++ ++ blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9); ++ blk_queue_segment_boundary(queue, -1UL); ++ blk_queue_dma_alignment(queue, dev->blk_size-1); ++ blk_queue_logical_block_size(queue, dev->blk_size); ++ ++ blk_queue_flush(queue, REQ_FLUSH); ++ ++ blk_queue_max_segments(queue, -1); ++ blk_queue_max_segment_size(queue, dev->bounce_size); ++ ++ gendisk = alloc_disk(PS3NFLASH_MINORS); ++ if (!gendisk) { ++ dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, ++ __LINE__); ++ error = -ENOMEM; ++ goto fail_cleanup; ++ } ++ ++ priv->gendisk[devidx] = gendisk; ++ gendisk->major = ps3nflash_major; ++ gendisk->first_minor = devidx * PS3NFLASH_MINORS; ++ gendisk->fops = &ps3nflash_fops; ++ gendisk->queue = queue; ++ gendisk->private_data = dev; ++ gendisk->driverfs_dev = &dev->sbd.core; ++ snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3NFLASH_NAME, ++ devidx+'a'); ++ priv->blocking_factor = dev->blk_size >> 9; ++ set_capacity(gendisk, ++ dev->regions[devidx].size*priv->blocking_factor); ++ ++ dev_info(&dev->sbd.core, ++ "%s (%llu MiB total, %lu MiB region)\n", ++ gendisk->disk_name, priv->raw_capacity >> 11, ++ get_capacity(gendisk) >> 11); ++ ++ add_disk(gendisk); ++ } ++ ++ return 0; ++ ++fail_cleanup: ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } ++ ps3stor_teardown(dev); ++fail_free_bounce: ++ kfree(dev->bounce_buf); ++fail_free_priv: ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++fail: ++ return error; ++} ++ ++static int ps3nflash_remove(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3nflash_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ unsigned int devidx; ++ ++ for (devidx = 0; devidx < dev->num_regions; devidx++) ++ { ++ if (priv->gendisk[devidx]) { ++ del_gendisk(priv->gendisk[devidx]); ++ put_disk(priv->gendisk[devidx]); ++ } ++ ++ if (priv->queue[devidx]) ++ blk_cleanup_queue(priv->queue[devidx]); ++ } ++ ++ ps3stor_teardown(dev); ++ kfree(dev->bounce_buf); ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++ return 0; ++} ++ ++static struct ps3_system_bus_driver ps3nflash = { ++ .match_id = PS3_MATCH_ID_STOR_NOR_FLASH, ++ .core.name = DEVICE_NAME, ++ .core.owner = THIS_MODULE, ++ .probe = ps3nflash_probe, ++ .remove = ps3nflash_remove, ++ .shutdown = ps3nflash_remove, ++}; ++ ++ ++static int __init ps3nflash_init(void) ++{ ++ int error; ++ ++ if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) ++ return -ENODEV; ++ ++ error = register_blkdev(0, DEVICE_NAME); ++ if (error <= 0) { ++ printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, ++ __LINE__, error); ++ return error; ++ } ++ ps3nflash_major = error; ++ ++ pr_info("%s:%u: registered block device major %d\n", __func__, ++ __LINE__, ps3nflash_major); ++ ++ error = ps3_system_bus_driver_register(&ps3nflash); ++ if (error) ++ unregister_blkdev(ps3nflash_major, DEVICE_NAME); ++ ++ return error; ++} ++ ++static void __exit ps3nflash_exit(void) ++{ ++ ps3_system_bus_driver_unregister(&ps3nflash); ++ unregister_blkdev(ps3nflash_major, DEVICE_NAME); ++} ++ ++module_init(ps3nflash_init); ++module_exit(ps3nflash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("PS3 NOR Flash Storage Driver"); ++MODULE_AUTHOR("Sony Corporation"); ++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_NOR_FLASH); diff --git a/0035-ps3-partition.patch b/0035-ps3-partition.patch new file mode 100644 index 0000000..a92764a --- /dev/null +++ b/0035-ps3-partition.patch @@ -0,0 +1,151 @@ +--- a/block/partitions/Kconfig 2013-10-01 14:29:39.166124545 +0200 ++++ b/block/partitions/Kconfig 2013-10-01 14:30:10.646126376 +0200 +@@ -261,6 +261,12 @@ + sysv68). + Otherwise, say N. + ++config PS3_PARTITION ++ bool "PS3 Partition support" ++ depends on PARTITION_ADVANCED ++ help ++ Say Y here if you would like to use PS3 hard disks under Linux. ++ + config CMDLINE_PARTITION + bool "Command line partition support" if PARTITION_ADVANCED + select CMDLINE_PARSER +--- a/block/partitions/Makefile 2012-09-06 18:41:11.858905657 +0200 ++++ b/block/partitions/Makefile 2012-09-06 18:56:12.622291395 +0200 +@@ -18,3 +18,4 @@ + obj-$(CONFIG_EFI_PARTITION) += efi.o + obj-$(CONFIG_KARMA_PARTITION) += karma.o + obj-$(CONFIG_SYSV68_PARTITION) += sysv68.o ++obj-$(CONFIG_PS3_PARTITION) += ps3.o +--- a/block/partitions/check.c 2013-10-01 14:33:08.996136753 +0200 ++++ b/block/partitions/check.c 2013-10-01 14:32:17.909467114 +0200 +@@ -34,6 +34,7 @@ + #include "efi.h" + #include "karma.h" + #include "sysv68.h" ++#include "ps3.h" + #include "cmdline.h" + + int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ +@@ -108,6 +109,9 @@ + #ifdef CONFIG_SYSV68_PARTITION + sysv68_partition, + #endif ++#ifdef CONFIG_PS3_PARTITION ++ ps3_partition, ++#endif + NULL + }; + +--- /dev/null 2012-09-06 18:32:42.202209340 +0200 ++++ b/block/partitions/ps3.h 2012-09-06 18:41:52.025574661 +0200 +@@ -0,0 +1,5 @@ ++/* ++ * fs/partitions/ps3.h ++ */ ++ ++int ps3_partition(struct parsed_partitions *state); +--- /dev/null 2012-09-06 18:32:42.202209340 +0200 ++++ b/block/partitions/ps3.c 2012-09-06 20:56:28.612711200 +0200 +@@ -0,0 +1,98 @@ ++/* ++ * fs/partitions/ps3.c ++ * ++ * Copyright (C) 2012 glevand ++ */ ++ ++#include "check.h" ++#include "ps3.h" ++ ++#define SECTOR_SIZE 512 ++#define MAX_ACL_ENTRIES 8 ++#define MAX_PARTITIONS 8 ++ ++#define MAGIC1 0x0FACE0FFULL ++#define MAGIC2 0xDEADFACEULL ++ ++struct p_acl_entry { ++ __be64 laid; ++ __be64 rights; ++}; ++ ++struct d_partition { ++ __be64 p_start; ++ __be64 p_size; ++ struct p_acl_entry p_acl[MAX_ACL_ENTRIES]; ++}; ++ ++struct disklabel { ++ u8 d_res1[16]; ++ __be64 d_magic1; ++ __be64 d_magic2; ++ __be64 d_res2; ++ __be64 d_res3; ++ struct d_partition d_partitions[MAX_PARTITIONS]; ++ u8 d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition)- 0x30]; ++}; ++ ++static bool ps3_read_disklabel(struct parsed_partitions *state, struct disklabel *label) ++{ ++ Sector sect; ++ unsigned char *data; ++ int i; ++ ++ for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) { ++ data = read_part_sector(state, i, §); ++ if (!data) ++ return (false); ++ ++ memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE); ++ ++ put_dev_sector(sect); ++ } ++ ++ return (true); ++} ++ ++int ps3_partition(struct parsed_partitions *state) ++{ ++ struct disklabel *label = NULL; ++ int slot = 1; ++ int result = -1; ++ int i; ++ ++ label = kmalloc(sizeof(struct disklabel), GFP_KERNEL); ++ if (!label) ++ goto out; ++ ++ if (!ps3_read_disklabel(state, label)) ++ goto out; ++ ++ result = 0; ++ ++ if ((be64_to_cpu(label->d_magic1) != MAGIC1) || ++ (be64_to_cpu(label->d_magic2) != MAGIC2)) ++ goto out; ++ ++ for (i = 0; i < MAX_PARTITIONS; i++) { ++ if (label->d_partitions[i].p_start && label->d_partitions[i].p_size) { ++ put_partition(state, slot, ++ be64_to_cpu(label->d_partitions[i].p_start), ++ be64_to_cpu(label->d_partitions[i].p_size)); ++ slot++; ++ } ++ } ++ ++ strlcat(state->pp_buf, "\n", PAGE_SIZE); ++ ++ kfree(label); ++ ++ return (1); ++ ++out: ++ ++ if (label) ++ kfree(label); ++ ++ return (result); ++} diff --git a/0040-ps3sysmgr-lpar-reboot.patch b/0040-ps3sysmgr-lpar-reboot.patch new file mode 100644 index 0000000..c65ab5e --- /dev/null +++ b/0040-ps3sysmgr-lpar-reboot.patch @@ -0,0 +1,11 @@ +--- a/drivers/ps3/ps3-sys-manager.c 2012-01-27 19:35:54.017195402 +0100 ++++ b/drivers/ps3/ps3-sys-manager.c 2012-01-27 19:36:18.990903099 +0100 +@@ -656,7 +656,7 @@ + ps3_vuart_cancel_async(dev); + + ps3_sys_manager_send_attr(dev, 0); +- ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT, ++ ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, + user_wake_sources); + + ps3_sys_manager_fin(dev); diff --git a/0050-ps3sysmgr-char-device.patch b/0050-ps3sysmgr-char-device.patch new file mode 100644 index 0000000..ca5430d --- /dev/null +++ b/0050-ps3sysmgr-char-device.patch @@ -0,0 +1,174 @@ +--- a/drivers/ps3/ps3-sys-manager.c 2012-01-26 20:35:02.242768746 +0100 ++++ b/drivers/ps3/ps3-sys-manager.c 2012-01-26 20:38:07.325547265 +0100 +@@ -3,6 +3,8 @@ + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. ++ * Copyright (C) 2011 graf_chokolo . ++ * Copyright (C) 2011, 2012 glevand . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -22,7 +24,12 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + ++#include + #include + #include + #include +@@ -43,6 +50,14 @@ + * specific payload. + */ + ++#define DEVICE_NAME "ps3sysmngr" ++ ++static struct ps3sm { ++ struct ps3_system_bus_device *dev; ++ struct miscdevice misc; ++ atomic_t misc_in_use; ++} *ps3sm; ++ + /** + * struct ps3_sys_manager_header - System manager message header. + * @version: Header version, currently 1. +@@ -706,6 +721,86 @@ + ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); + } + ++static int ps3_sys_manager_misc_open(struct inode *inode, struct file *file) ++{ ++ if (atomic_inc_return(&ps3sm->misc_in_use) == 1) ++ ps3_vuart_cancel_async(ps3sm->dev); ++ ++ return 0; ++} ++ ++static int ps3_sys_manager_misc_release(struct inode *inode, struct file *file) ++{ ++ if (atomic_dec_and_test(&ps3sm->misc_in_use)) ++ ps3_vuart_read_async(ps3sm->dev, PS3_SM_RX_MSG_LEN_MIN); ++ ++ return 0; ++} ++ ++static ssize_t ps3_sys_manager_misc_read(struct file *file, char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ result = ps3_vuart_read(ps3sm->dev, buf, count); ++ if (result) ++ goto out; ++ ++ if (copy_to_user(usrbuf, buf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static ssize_t ps3_sys_manager_misc_write(struct file *file, const char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, usrbuf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = ps3_vuart_write(ps3sm->dev, buf, count); ++ if (result) ++ goto out; ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static const struct file_operations ps3_sys_manager_misc_fops = { ++ .owner = THIS_MODULE, ++ .open = ps3_sys_manager_misc_open, ++ .release = ps3_sys_manager_misc_release, ++ .read = ps3_sys_manager_misc_read, ++ .write = ps3_sys_manager_misc_write, ++}; ++ + static int ps3_sys_manager_probe(struct ps3_system_bus_device *dev) + { + int result; +@@ -727,12 +822,47 @@ + result = ps3_vuart_read_async(dev, PS3_SM_RX_MSG_LEN_MIN); + BUG_ON(result); + ++ ps3sm = kzalloc(sizeof(*ps3sm), GFP_KERNEL); ++ if (!ps3sm) ++ goto skip_misc; ++ ++ ps3sm->dev = dev; ++ ++ ps3sm->misc.parent = &dev->core; ++ ps3sm->misc.minor = MISC_DYNAMIC_MINOR, ++ ps3sm->misc.name = DEVICE_NAME, ++ ps3sm->misc.fops = &ps3_sys_manager_misc_fops, ++ atomic_set(&ps3sm->misc_in_use, 0); ++ ++ result = misc_register(&ps3sm->misc); ++ if (result) { ++ dev_err(&dev->core, "%s:%u: misc_register failed %d\n", ++ __func__, __LINE__, result); ++ kfree(ps3sm); ++ ps3sm = NULL; ++ goto skip_misc; ++ } ++ ++ dev_info(&dev->core, "%s:%u: registered misc device %d\n", ++ __func__, __LINE__, ps3sm->misc.minor); ++ ++skip_misc: ++ ++ result = 0; ++ + return result; + } + + static int ps3_sys_manager_remove(struct ps3_system_bus_device *dev) + { + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); ++ ++ if (ps3sm) { ++ misc_deregister(&ps3sm->misc); ++ kfree(ps3sm); ++ ps3sm = NULL; ++ } ++ + return 0; + } + diff --git a/0060-ps3avmgr-char-device.patch b/0060-ps3avmgr-char-device.patch new file mode 100644 index 0000000..4c4ec20 --- /dev/null +++ b/0060-ps3avmgr-char-device.patch @@ -0,0 +1,143 @@ +--- a/drivers/ps3/ps3av.c 2013-03-07 22:13:51.309374686 +0100 ++++ b/drivers/ps3/ps3av.c 2013-03-07 22:14:15.449376092 +0100 +@@ -25,6 +25,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -35,6 +37,8 @@ + #define BUFSIZE 4096 /* vuart buf size */ + #define PS3AV_BUF_SIZE 512 /* max packet size */ + ++#define DEVICE_NAME "ps3avmngr" ++ + static int safe_mode; + + static int timeout = 5000; /* in msec ( 5 sec ) */ +@@ -60,6 +64,8 @@ + struct ps3av_reply_hdr reply_hdr; + u8 raw[PS3AV_BUF_SIZE]; + } recv_buf; ++ struct miscdevice misc; ++ int misc_ok; + } *ps3av; + + /* color space */ +@@ -932,6 +938,68 @@ + } + EXPORT_SYMBOL_GPL(ps3av_audio_mute); + ++static ssize_t ps3av_misc_read(struct file *file, char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ result = ps3_vuart_read(ps3av->dev, buf, count); ++ if (result) ++ goto out; ++ ++ if (copy_to_user(usrbuf, buf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static ssize_t ps3av_misc_write(struct file *file, const char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, usrbuf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = ps3_vuart_write(ps3av->dev, buf, count); ++ if (result) ++ goto out; ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static const struct file_operations ps3av_misc_fops = { ++ .owner = THIS_MODULE, ++ .read = ps3av_misc_read, ++ .write = ps3av_misc_write, ++}; ++ + static int ps3av_probe(struct ps3_system_bus_device *dev) + { + int res; +@@ -1003,11 +1071,34 @@ + ps3av->ps3av_mode = id; + mutex_unlock(&ps3av->mutex); + ++ ps3av->misc.parent = &dev->core; ++ ps3av->misc.minor = MISC_DYNAMIC_MINOR, ++ ps3av->misc.name = DEVICE_NAME, ++ ps3av->misc.fops = &ps3av_misc_fops, ++ ++ res = misc_register(&ps3av->misc); ++ if (res) { ++ dev_err(&dev->core, "%s:%u: misc_register failed %d\n", ++ __func__, __LINE__, res); ++ goto skip_misc; ++ } ++ ++ ps3av->misc_ok = 1; ++ ++ dev_info(&dev->core, "%s:%u: registered misc device %d\n", ++ __func__, __LINE__, ps3av->misc.minor); ++ ++skip_misc: ++ + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); + + return 0; + + fail: ++ ++ if (ps3av->misc_ok) ++ misc_deregister(&ps3av->misc); ++ + kfree(ps3av); + ps3av = NULL; + return res; +@@ -1017,6 +1108,9 @@ + { + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); + if (ps3av) { ++ if (ps3av->misc_ok) ++ misc_deregister(&ps3av->misc); ++ + ps3av_cmd_fin(); + if (ps3av->wq) + destroy_workqueue(ps3av->wq); diff --git a/0070-ps3dispmgr.patch b/0070-ps3dispmgr.patch new file mode 100644 index 0000000..a74b1a3 --- /dev/null +++ b/0070-ps3dispmgr.patch @@ -0,0 +1,328 @@ +--- a/drivers/ps3/ps3-vuart.c 2012-01-12 20:42:45.000000000 +0100 ++++ b/drivers/ps3/ps3-vuart.c 2012-01-26 18:13:18.981200186 +0100 +@@ -39,6 +39,7 @@ + * vuart - An inter-partition data link service. + * port 0: PS3 AV Settings. + * port 2: PS3 System Manager. ++ * port 10: PS3 Dispatcher Manager. + * + * The vuart provides a bi-directional byte stream data link between logical + * partitions. Its primary role is as a communications link between the guest +@@ -46,7 +47,7 @@ + * connections other than those listed. + */ + +-enum {PORT_COUNT = 3,}; ++enum {PORT_COUNT = 11,}; + + enum vuart_param { + PARAM_TX_TRIGGER = 0, +@@ -926,7 +927,7 @@ + + vuart_bus_priv.use_count++; + +- BUG_ON(vuart_bus_priv.use_count > 2); ++ BUG_ON(vuart_bus_priv.use_count > 3); + + if (vuart_bus_priv.use_count != 1) + return 0; +--- a/arch/powerpc/include/asm/ps3.h 2012-01-26 14:09:41.132438036 +0100 ++++ b/arch/powerpc/include/asm/ps3.h 2012-01-26 14:11:27.368200391 +0100 +@@ -327,6 +327,7 @@ + PS3_MATCH_ID_GPU = 10, + PS3_MATCH_ID_LPM = 11, + PS3_MATCH_ID_STOR_NOR_FLASH = 12, ++ PS3_MATCH_ID_DISP_MANAGER = 13, + }; + + enum ps3_match_sub_id { +@@ -347,6 +348,7 @@ + #define PS3_MODULE_ALIAS_GPU_RAMDISK "ps3:10:2" + #define PS3_MODULE_ALIAS_LPM "ps3:11:0" + #define PS3_MODULE_ALIAS_STOR_NOR_FLASH "ps3:12:0" ++#define PS3_MODULE_ALIAS_DISP_MANAGER "ps3:13:0" + + enum ps3_system_bus_device_type { + PS3_DEVICE_TYPE_IOC0 = 1, +--- a/arch/powerpc/platforms/ps3/system-bus.c 2012-01-26 14:09:41.144403930 +0100 ++++ b/arch/powerpc/platforms/ps3/system-bus.c 2012-01-26 14:17:05.896702636 +0100 +@@ -183,6 +183,7 @@ + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: ++ case PS3_MATCH_ID_DISP_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, +@@ -222,6 +223,7 @@ + + case PS3_MATCH_ID_AV_SETTINGS: + case PS3_MATCH_ID_SYSTEM_MANAGER: ++ case PS3_MATCH_ID_DISP_MANAGER: + pr_debug("%s:%d: unsupported match_id: %u\n", __func__, + __LINE__, dev->match_id); + pr_debug("%s:%d: bus_id: %llu\n", __func__, __LINE__, +--- a/arch/powerpc/platforms/ps3/device-init.c 2012-01-26 14:09:41.144403930 +0100 ++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-01-26 14:25:10.440202587 +0100 +@@ -426,6 +426,7 @@ + { + int result; + unsigned int port_number; ++ u64 lpar_id, laid, junk; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + +@@ -444,6 +445,27 @@ + port_number); + WARN_ON(result); + ++ result = lv1_get_logical_partition_id(&lpar_id); ++ if (result) ++ goto out; ++ ++ result = lv1_read_repository_node(1, 0x0000000073730000ul /* ss */, ++ 0x6c61696400000000ul /* laid */, ++ lpar_id, ++ 0, &laid, &junk); ++ if (result) ++ goto out; ++ ++ if (laid == 0x1070000002000001ul) { ++ port_number = 10; ++ result = ps3_setup_vuart_device(PS3_MATCH_ID_DISP_MANAGER, ++ port_number); ++ } ++ ++out: ++ ++ WARN_ON(result); ++ + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return result; + } +--- a/drivers/ps3/Makefile 2012-01-12 20:42:45.000000000 +0100 ++++ b/drivers/ps3/Makefile 2012-01-26 17:40:36.400861141 +0100 +@@ -5,3 +5,4 @@ + obj-$(CONFIG_PS3_SYS_MANAGER) += ps3-sys-manager.o + obj-$(CONFIG_PS3_STORAGE) += ps3stor_lib.o + obj-$(CONFIG_PS3_LPM) += ps3-lpm.o ++obj-$(CONFIG_PS3_DISP_MANAGER) += ps3-disp-manager.o +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-01-26 17:37:55.331589081 +0100 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-01-26 17:42:48.049593001 +0100 +@@ -88,6 +88,17 @@ + This support is required for system control. In + general, all users will say Y or M. + ++config PS3_DISP_MANAGER ++ depends on PPC_PS3 ++ tristate "PS3 Dispatcher Manager driver" if PS3_ADVANCED ++ select PS3_VUART ++ default y ++ help ++ Include support for the PS3 Dispatcher Manager. ++ ++ This support is required to access the PS3 SS services. ++ In general, all users will say Y or M. ++ + config PS3_STORAGE + depends on PPC_PS3 + tristate +--- /dev/null 2011-11-07 10:16:29.227396409 +0100 ++++ b/drivers/ps3/ps3-disp-manager.c 2012-01-26 15:08:35.100453068 +0100 +@@ -0,0 +1,196 @@ ++/* ++ * PS3 Dispatcher Manager. ++ * ++ * Copyright (C) 2011 graf_chokolo . ++ * Copyright (C) 2011, 2012 glevand . ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "vuart.h" ++ ++#define DEVICE_NAME "ps3dispmngr" ++ ++static struct ps3dm { ++ struct ps3_system_bus_device *dev; ++ struct miscdevice misc; ++} *ps3dm; ++ ++static ssize_t ps3_disp_manager_read(struct file *file, char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ result = ps3_vuart_read(ps3dm->dev, buf, count); ++ if (result) ++ goto out; ++ ++ if (copy_to_user(usrbuf, buf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static ssize_t ps3_disp_manager_write(struct file *file, const char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ char *buf; ++ int result; ++ ++ buf = kmalloc(count, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, usrbuf, count)) { ++ result = -EFAULT; ++ goto out; ++ } ++ ++ result = ps3_vuart_write(ps3dm->dev, buf, count); ++ if (result) ++ goto out; ++ ++ result = count; ++ ++out: ++ ++ kfree(buf); ++ ++ return result; ++} ++ ++static const struct file_operations ps3_disp_manager_fops = { ++ .owner = THIS_MODULE, ++ .read = ps3_disp_manager_read, ++ .write = ps3_disp_manager_write, ++}; ++ ++static int ps3_disp_manager_probe(struct ps3_system_bus_device *dev) ++{ ++ int result; ++ ++ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); ++ ++ if (ps3dm) { ++ dev_err(&dev->core, "Only one Dispatcher Manager is supported\n"); ++ return -EBUSY; ++ } ++ ++ ps3dm = kzalloc(sizeof(*ps3dm), GFP_KERNEL); ++ if (!ps3dm) ++ return -ENOMEM; ++ ++ ps3dm->dev = dev; ++ ++ ps3dm->misc.parent = &dev->core; ++ ps3dm->misc.minor = MISC_DYNAMIC_MINOR, ++ ps3dm->misc.name = DEVICE_NAME, ++ ps3dm->misc.fops = &ps3_disp_manager_fops, ++ ++ result = misc_register(&ps3dm->misc); ++ if (result) { ++ dev_err(&dev->core, "%s:%u: misc_register failed %d\n", ++ __func__, __LINE__, result); ++ goto fail; ++ } ++ ++ dev_info(&dev->core, "%s:%u: registered misc device %d\n", ++ __func__, __LINE__, ps3dm->misc.minor); ++ ++ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); ++ ++ return 0; ++ ++fail: ++ ++ kfree(ps3dm); ++ ps3dm = NULL; ++ ++ return result; ++} ++ ++static int ps3_disp_manager_remove(struct ps3_system_bus_device *dev) ++{ ++ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); ++ ++ if (ps3dm) { ++ misc_deregister(&ps3dm->misc); ++ kfree(ps3dm); ++ ps3dm = NULL; ++ } ++ ++ return 0; ++} ++ ++static void ps3_disp_manager_shutdown(struct ps3_system_bus_device *dev) ++{ ++ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); ++} ++ ++static struct ps3_vuart_port_driver ps3_disp_manager = { ++ .core.match_id = PS3_MATCH_ID_DISP_MANAGER, ++ .core.core.name = "ps3_disp_manager", ++ .probe = ps3_disp_manager_probe, ++ .remove = ps3_disp_manager_remove, ++ .shutdown = ps3_disp_manager_shutdown, ++}; ++ ++static int __init ps3_disp_manager_init(void) ++{ ++ if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) ++ return -ENODEV; ++ ++ return ps3_vuart_port_driver_register(&ps3_disp_manager); ++} ++ ++static void __exit ps3_disp_manager_exit(void) ++{ ++ pr_debug(" -> %s:%d\n", __func__, __LINE__); ++ ++ ps3_vuart_port_driver_unregister(&ps3_disp_manager); ++ ++ pr_debug(" <- %s:%d\n", __func__, __LINE__); ++} ++ ++module_init(ps3_disp_manager_init); ++module_exit(ps3_disp_manager_exit); ++ ++MODULE_AUTHOR("glevand"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("PS3 Dispatcher Manager"); ++MODULE_ALIAS(PS3_MODULE_ALIAS_DISP_MANAGER); diff --git a/0080-ps3rom-vendor-specific-command.patch b/0080-ps3rom-vendor-specific-command.patch new file mode 100644 index 0000000..685f6c6 --- /dev/null +++ b/0080-ps3rom-vendor-specific-command.patch @@ -0,0 +1,63 @@ +--- a/drivers/scsi/ps3rom.c 2012-02-13 18:45:43.735156562 +0100 ++++ b/drivers/scsi/ps3rom.c 2012-02-13 18:45:54.835348606 +0100 +@@ -40,6 +40,8 @@ + + #define PS3ROM_MAX_SECTORS (BOUNCE_SIZE >> 9) + ++#define PS3ROM_VENDOR_SPECIFIC_OPCODE 0xfd ++ + + struct ps3rom_private { + struct ps3_storage_device *dev; +@@ -165,6 +167,40 @@ + return 0; + } + ++static int ps3rom_vendor_specific_request(struct ps3_storage_device *dev, ++ struct scsi_cmnd *cmd) ++{ ++ unsigned char opcode = cmd->cmnd[1]; ++ int res; ++ ++ dev_dbg(&dev->sbd.core, "%s:%u: send vendor-specific command 0x%02x\n", __func__, ++ __LINE__, opcode); ++ ++ if (cmd->sc_data_direction == DMA_TO_DEVICE) ++ scsi_sg_copy_to_buffer(cmd, dev->bounce_buf, dev->bounce_size); ++ ++ res = lv1_storage_send_device_command(dev->sbd.dev_id, ++ opcode, ++ dev->bounce_lpar, scsi_bufflen(cmd), ++ dev->bounce_lpar, dev->bounce_size, ++ &dev->tag); ++ if (res == LV1_DENIED_BY_POLICY) { ++ dev_dbg(&dev->sbd.core, ++ "%s:%u: vendor-specific command 0x%02x denied by policy\n", ++ __func__, __LINE__, opcode); ++ return DID_ERROR << 16; ++ } ++ ++ if (res) { ++ dev_err(&dev->sbd.core, ++ "%s:%u: vendor-specific command 0x%02x failed %d\n", __func__, ++ __LINE__, opcode, res); ++ return DID_ERROR << 16; ++ } ++ ++ return 0; ++} ++ + static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd) + { + return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 | +@@ -254,6 +290,10 @@ + srb10_len(cmd)); + break; + ++ case PS3ROM_VENDOR_SPECIFIC_OPCODE: ++ res = ps3rom_vendor_specific_request(dev, cmd); ++ break; ++ + default: + res = ps3rom_atapi_request(dev, cmd); + break; diff --git a/0090-spu-enum-shared-param.patch b/0090-spu-enum-shared-param.patch new file mode 100644 index 0000000..539623b --- /dev/null +++ b/0090-spu-enum-shared-param.patch @@ -0,0 +1,48 @@ +--- a/arch/powerpc/include/asm/spu.h 2012-03-01 01:32:49.000000000 +0100 ++++ b/arch/powerpc/include/asm/spu.h 2012-03-05 17:49:44.083370504 +0100 +@@ -198,6 +198,8 @@ + + extern struct cbe_spu_info cbe_spu_info[]; + ++extern int spu_enum_shared(void); ++ + void spu_init_channels(struct spu *spu); + void spu_irq_setaffinity(struct spu *spu, int cpu); + +--- a/arch/powerpc/platforms/cell/spu_base.c 2012-03-01 01:32:49.000000000 +0100 ++++ b/arch/powerpc/platforms/cell/spu_base.c 2012-03-05 17:52:47.659547098 +0100 +@@ -40,6 +40,10 @@ + #include + #include + ++static int enum_shared = 0; ++module_param(enum_shared, int, S_IRUGO); ++MODULE_PARM_DESC(enum_shared, "Enumerate shared SPUs"); ++ + const struct spu_management_ops *spu_management_ops; + EXPORT_SYMBOL_GPL(spu_management_ops); + +@@ -484,6 +488,12 @@ + free_irq(spu->irqs[2], spu); + } + ++int spu_enum_shared(void) ++{ ++ return (enum_shared); ++} ++EXPORT_SYMBOL_GPL(spu_enum_shared); ++ + void spu_init_channels(struct spu *spu) + { + static const struct { +--- a/arch/powerpc/platforms/ps3/spu.c 2012-03-01 01:32:49.000000000 +0100 ++++ b/arch/powerpc/platforms/ps3/spu.c 2012-03-05 17:54:01.800664787 +0100 +@@ -421,7 +421,7 @@ + if (result) + break; + +- if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE) { ++ if (resource_type == PS3_SPU_RESOURCE_TYPE_EXCLUSIVE || spu_enum_shared()) { + result = fn((void*)(unsigned long)resource_id); + + if (result) diff --git a/0100-lv1call-repo-node-lparid-param.patch b/0100-lv1call-repo-node-lparid-param.patch new file mode 100644 index 0000000..b27e20c --- /dev/null +++ b/0100-lv1call-repo-node-lparid-param.patch @@ -0,0 +1,16 @@ +--- a/arch/powerpc/include/asm/lv1call.h 2012-01-26 01:39:32.000000000 +0100 ++++ b/arch/powerpc/include/asm/lv1call.h 2012-01-31 20:52:04.932882025 +0100 +@@ -263,10 +263,10 @@ + LV1_CALL(configure_execution_time_variable, 1, 0, 77 ) + LV1_CALL(get_spe_irq_outlet, 2, 1, 78 ) + LV1_CALL(set_spe_privilege_state_area_1_register, 3, 0, 79 ) +-LV1_CALL(create_repository_node, 6, 0, 90 ) ++LV1_CALL(create_repository_node, 7, 0, 90 ) + LV1_CALL(read_repository_node, 5, 2, 91 ) +-LV1_CALL(write_repository_node, 6, 0, 92 ) +-LV1_CALL(delete_repository_node, 4, 0, 93 ) ++LV1_CALL(write_repository_node, 7, 0, 92 ) ++LV1_CALL(delete_repository_node, 5, 0, 93 ) + LV1_CALL(read_htab_entries, 2, 5, 95 ) + LV1_CALL(set_dabr, 2, 0, 96 ) + LV1_CALL(get_total_execution_time, 2, 1, 103 ) diff --git a/0110-lv1call-add-hvcalls-114-115.patch b/0110-lv1call-add-hvcalls-114-115.patch new file mode 100644 index 0000000..3b8935b --- /dev/null +++ b/0110-lv1call-add-hvcalls-114-115.patch @@ -0,0 +1,11 @@ +--- a/arch/powerpc/include/asm/lv1call.h 2012-02-03 21:39:51.000000000 +0100 ++++ b/arch/powerpc/include/asm/lv1call.h 2012-02-05 17:34:26.973058855 +0100 +@@ -270,6 +270,8 @@ + LV1_CALL(read_htab_entries, 2, 5, 95 ) + LV1_CALL(set_dabr, 2, 0, 96 ) + LV1_CALL(get_total_execution_time, 2, 1, 103 ) ++LV1_CALL(undocumented_function_114, 3, 1, 114 ) ++LV1_CALL(undocumented_function_115, 1, 0, 115 ) + LV1_CALL(allocate_io_segment, 3, 1, 116 ) + LV1_CALL(release_io_segment, 2, 0, 117 ) + LV1_CALL(construct_io_irq_outlet, 1, 1, 120 ) diff --git a/0120-lv1call-add-storage-region-hvcalls.patch b/0120-lv1call-add-storage-region-hvcalls.patch new file mode 100644 index 0000000..46b662c --- /dev/null +++ b/0120-lv1call-add-storage-region-hvcalls.patch @@ -0,0 +1,13 @@ +--- a/arch/powerpc/include/asm/lv1call.h 2012-02-07 18:22:20.218981765 +0100 ++++ b/arch/powerpc/include/asm/lv1call.h 2012-02-08 19:44:40.636013978 +0100 +@@ -327,6 +327,10 @@ + LV1_CALL(storage_write, 6, 1, 246 ) + LV1_CALL(storage_send_device_command, 6, 1, 248 ) + LV1_CALL(storage_get_async_status, 1, 2, 249 ) ++LV1_CALL(storage_create_region, 5, 2, 250 ) ++LV1_CALL(storage_delete_region, 2, 1, 251 ) ++LV1_CALL(storage_set_region_acl, 4, 1, 252 ) ++LV1_CALL(storage_get_region_acl, 3, 2, 253 ) + LV1_CALL(storage_check_async_status, 2, 1, 254 ) + LV1_CALL(panic, 1, 0, 255 ) + LV1_CALL(construct_lpm, 6, 3, 140 ) diff --git a/0130-ps3physmem.patch b/0130-ps3physmem.patch new file mode 100644 index 0000000..d3b25da --- /dev/null +++ b/0130-ps3physmem.patch @@ -0,0 +1,211 @@ +--- a/drivers/char/Makefile 2012-02-03 21:39:51.000000000 +0100 ++++ b/drivers/char/Makefile 2012-02-05 17:51:57.339262786 +0100 +@@ -65,3 +65,5 @@ + js-rtc-y = rtc.o + + obj-$(CONFIG_TILE_SROM) += tile-srom.o ++ ++obj-$(CONFIG_PS3_PHYSMEM) += ps3physmem.o +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-02-04 21:13:42.539806178 +0100 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-02-05 17:54:38.698430717 +0100 +@@ -179,6 +179,12 @@ + profiling support of the Cell processor with programs like + oprofile and perfmon2, then say Y or M, otherwise say N. + ++config PS3_PHYSMEM ++ tristate "PS3 Physical Memory Driver" ++ depends on PPC_PS3 ++ help ++ This driver allows you direct access to the PS3 physical memory. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- /dev/null 2012-02-05 10:06:29.087361680 +0100 ++++ b/drivers/char/ps3physmem.c 2012-02-05 17:50:30.897909862 +0100 +@@ -0,0 +1,185 @@ ++/* ++ * PS3 Physical Memory Driver ++ * ++ * Copyright (C) 2011 graf_chokolo ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static unsigned long mem_start = 0; ++module_param(mem_start, ulong, 0); ++ ++static unsigned long mem_size = 256 * 1024 * 1024; ++module_param(mem_size, ulong, 0); ++ ++static unsigned long mem_pagesize = 24; /* 16MB */ ++module_param(mem_pagesize, ulong, 0); ++ ++static u64 ps3physmem_lpar; ++static char *ps3physmem_virt; ++ ++static ssize_t ps3physmem_read(struct file *file, char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ if (*pos + count > mem_size) ++ count = mem_size - *pos; ++ ++ if (!count) ++ return (0); ++ ++ if (copy_to_user(usrbuf, ps3physmem_virt + *pos, count)) ++ return (-EFAULT); ++ ++ *pos += count; ++ ++ return (count); ++} ++ ++static ssize_t ps3physmem_write(struct file *file, const char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ if (*pos + count > mem_size) ++ count = mem_size - *pos; ++ ++ if (!count) ++ return (0); ++ ++ if (copy_from_user(ps3physmem_virt + *pos, usrbuf, count)) ++ return (-EFAULT); ++ ++ *pos += count; ++ ++ return (count); ++} ++ ++static void ps3physmem_vma_open(struct vm_area_struct *vma) ++{ ++} ++ ++static void ps3physmem_vma_close(struct vm_area_struct *vma) ++{ ++} ++ ++static struct vm_operations_struct ps3physmem_vm_ops = { ++ .open = ps3physmem_vma_open, ++ .close = ps3physmem_vma_close, ++}; ++ ++static int ps3physmem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ unsigned long size, pfn; ++ int res; ++ ++ size = vma->vm_end - vma->vm_start; ++ ++ if (((vma->vm_pgoff << PAGE_SHIFT) + size) > mem_size) ++ return (-EINVAL); ++ ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ pfn = ((unsigned long) ps3physmem_virt >> PAGE_SHIFT) + vma->vm_pgoff; ++ ++ res = io_remap_pfn_range(vma, vma->vm_start, pfn, size, ++ vma->vm_page_prot); ++ if (res) ++ return (res); ++ ++ vma->vm_ops = &ps3physmem_vm_ops; ++ ++ ps3physmem_vma_open(vma); ++ ++ return (0); ++} ++ ++static const struct file_operations ps3physmem_fops = { ++ .owner = THIS_MODULE, ++ .read = ps3physmem_read, ++ .write = ps3physmem_write, ++ .mmap = ps3physmem_mmap, ++}; ++ ++static struct miscdevice ps3physmem_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ps3physmem", ++ .fops = &ps3physmem_fops, ++}; ++ ++static int __init ps3physmem_init(void) ++{ ++ int res; ++ ++ res = lv1_undocumented_function_114(mem_start, ++ mem_pagesize, mem_size, &ps3physmem_lpar); ++ if (res) { ++ pr_info("%s:%u: lv1_undocumented_function_114 failed %d\n", ++ __func__, __LINE__, res); ++ return (-ENXIO); ++ } ++ ++ ps3physmem_virt = ioremap_nocache(ps3physmem_lpar, mem_size); ++ if (!ps3physmem_virt) { ++ pr_info("%s:%u: ioremap_nocache failed\n", __func__, __LINE__); ++ goto destroy_lpar; ++ } ++ ++ res = misc_register(&ps3physmem_misc); ++ if (res) { ++ pr_info("%s:%u: misc_register failed %d\n", ++ __func__, __LINE__, res); ++ goto unmap_lpar; ++ } ++ ++ return (0); ++ ++unmap_lpar: ++ ++ iounmap((void *) ps3physmem_virt); ++ ++destroy_lpar: ++ ++ lv1_undocumented_function_115(ps3physmem_lpar); ++ ++ return (res); ++} ++ ++static void __exit ps3physmem_exit(void) ++{ ++ misc_deregister(&ps3physmem_misc); ++ ++ iounmap((void *) ps3physmem_virt); ++ ++ lv1_undocumented_function_115(ps3physmem_lpar); ++} ++ ++module_init(ps3physmem_init); ++module_exit(ps3physmem_exit); ++ ++MODULE_AUTHOR("glevand"); ++MODULE_DESCRIPTION("PS3 Physical Memory Driver"); ++MODULE_LICENSE("GPL"); diff --git a/0140-ps3strgmngr.patch b/0140-ps3strgmngr.patch new file mode 100644 index 0000000..057ccd2 --- /dev/null +++ b/0140-ps3strgmngr.patch @@ -0,0 +1,565 @@ +--- a/drivers/char/Makefile 2012-02-08 19:49:18.736981163 +0100 ++++ b/drivers/char/Makefile 2012-02-10 18:43:23.927197754 +0100 +@@ -67,3 +67,4 @@ + obj-$(CONFIG_TILE_SROM) += tile-srom.o + + obj-$(CONFIG_PS3_PHYSMEM) += ps3physmem.o ++obj-$(CONFIG_PS3_STRGMNGR) += ps3strgmngr.o +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-02-08 19:49:18.736981163 +0100 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-02-10 18:46:54.907113221 +0100 +@@ -185,6 +185,13 @@ + help + This driver allows you direct access to the PS3 physical memory. + ++config PS3_STRGMNGR ++ tristate "PS3 Storage Manager Driver" ++ depends on PPC_PS3 ++ help ++ This driver allows you to create/delete/modify regions ++ on PS3 storage devices. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- a/arch/powerpc/include/uapi/asm/Kbuild 2012-12-11 10:00:27.000000000 -0900 ++++ b/arch/powerpc/include/uapi/asm/Kbuild 2012-12-11 10:00:46.000000000 -0900 +@@ -42,3 +42,4 @@ + header-y += types.h + header-y += ucontext.h + header-y += unistd.h ++header-y += ps3strgmngr.h +--- /dev/null 2012-02-10 17:03:27.997801619 +0100 ++++ b/arch/powerpc/include/asm/ps3strgmngr.h 2012-02-10 18:49:42.806463666 +0100 +@@ -0,0 +1,101 @@ ++/* ++ * PS3 Storage Manager Driver ++ * ++ * Copyright (C) 2011 graf_chokolo ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef _PS3STRGMNGR_H ++#define _PS3STRGMNGR_H ++ ++#include ++#include ++ ++#define PS3STRGMNGR_MAX_ACL_ENTRIES 8 ++#define PS3STRGMNGR_MAX_REGIONS 8 ++#define PS3STRGMNGR_MAX_DEVICES 8 ++ ++#define PS3STRGMNGR_CTL_GET_DEVICES _IOR('s', 0, int) ++#define PS3STRGMNGR_CTL_CREATE_REGION _IOWR('s', 1, int) ++#define PS3STRGMNGR_CTL_DELETE_REGION _IOW('s', 2, int) ++#define PS3STRGMNGR_CTL_SET_REGION_ACL_ENTRY _IOW('s', 3, int) ++ ++enum ps3strgmngr_devtype { ++ PS3STRGMNGR_DEVTYPE_DISK = 0, ++ PS3STRGMNGR_DEVTYPE_CDROM, ++ PS3STRGMNGR_DEVTYPE_FLASH, ++ PS3STRGMNGR_DEVTYPE_NOR_FLASH, ++}; ++ ++enum ps3strgmngr_region_access_rights { ++ PS3STRGMNGR_REGION_READ = (1ull << 0), ++ PS3STRGMNGR_REGION_WRITE = (1ull << 1), ++}; ++ ++struct ps3strgmngr_acl_entry { ++ __u64 laid; ++ __u64 access_rights; /* enum ps3strgmngr_region_access_rights */ ++}; ++ ++struct ps3strgmngr_region { ++ __u64 id; ++ __u64 start_block; ++ __u64 nr_blocks; ++ __u64 nr_acl_entries; ++ struct ps3strgmngr_acl_entry acl_entry[PS3STRGMNGR_MAX_ACL_ENTRIES]; ++}; ++ ++struct ps3strgmngr_device { ++ __u64 type; /* enum ps3strgmngr_devtype */ ++ __u64 id; ++ __u64 block_size; ++ __u64 nr_blocks; ++ __u64 nr_regions; ++ struct ps3strgmngr_region region[PS3STRGMNGR_MAX_REGIONS]; ++}; ++ ++struct ps3strgmngr_ctl_get_devices { ++ /* out */ ++ __u64 nr_devices; ++ struct ps3strgmngr_device device[PS3STRGMNGR_MAX_DEVICES]; ++}; ++ ++struct ps3strgmngr_ctl_create_region { ++ /* in */ ++ __u64 device_id; ++ __u64 start_block; ++ __u64 nr_blocks; ++ __u64 laid; ++ /* out */ ++ __u64 region_id; ++}; ++ ++struct ps3strgmngr_ctl_delete_region { ++ /* in */ ++ __u64 device_id; ++ __u64 region_id; ++}; ++ ++struct ps3strgmngr_ctl_set_region_acl_entry { ++ /* in */ ++ __u64 device_id; ++ __u64 region_id; ++ __u64 laid; ++ __u64 access_rights; ++}; ++ ++#endif /* _PS3STRGMNGR_H */ +--- /dev/null 2012-02-10 17:03:27.997801619 +0100 ++++ b/drivers/char/ps3strgmngr.c 2012-02-10 23:29:41.429492084 +0100 +@@ -0,0 +1,428 @@ ++/* ++ * PS3 Storage Manager Driver ++ * ++ * Copyright (C) 2011 graf_chokolo ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define STORAGE_BUS_TYPE 5 ++ ++#define STORAGE_DEV_TYPE_DISK 0 ++#define STORAGE_DEV_TYPE_CDROM 5 ++#define STORAGE_DEV_TYPE_FLASH 14 ++#define STORAGE_DEV_TYPE_NOR_FLASH 254 ++ ++static int ps3strgmngr_read_acl(u64 dev_id, struct ps3strgmngr_region *rgn) ++{ ++ struct ps3strgmngr_acl_entry *acl_entry; ++ unsigned int acl_entry_index; ++ int res; ++ ++ rgn->nr_acl_entries = 0; ++ acl_entry = rgn->acl_entry; ++ ++ for (acl_entry_index = 0; acl_entry_index < PS3STRGMNGR_MAX_ACL_ENTRIES; acl_entry_index++) { ++ res = lv1_storage_get_region_acl(dev_id, rgn->id, acl_entry_index, ++ &acl_entry->laid, &acl_entry->access_rights); ++ if (res) ++ continue; ++ ++ rgn->nr_acl_entries++; ++ acl_entry++; ++ } ++ ++ return (0); ++} ++ ++static int ps3strgmngr_read_region(u64 bus_index, u64 dev_index, u64 dev_id, u64 dev_type, ++ u64 rgn_index, struct ps3strgmngr_region *rgn) ++{ ++ u64 flash_ext_flag, junk; ++ int res; ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x726567696f6e0000ul | rgn_index /* region */, ++ 0x6964000000000000ul /* id */, ++ &rgn->id, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ if (dev_type == STORAGE_DEV_TYPE_DISK) { ++ res = lv1_read_repository_node(1, 0x0000000073797300ul /* sys */, ++ 0x666c617368000000ul /* flash */, ++ 0x6578740000000000ul /* ext */, ++ 0, &flash_ext_flag, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ if (!(flash_ext_flag & 0x1) && (rgn->id > 0)) ++ rgn->id += 1; ++ } ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x726567696f6e0000ul | rgn_index /* region */, ++ 0x7374617274000000ul /* start */, ++ &rgn->start_block, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x726567696f6e0000ul | rgn_index /* region */, ++ 0x73697a6500000000ul /* size */, ++ &rgn->nr_blocks, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = ps3strgmngr_read_acl(dev_id, rgn); ++ if (res) ++ return (res); ++ ++ return (0); ++} ++ ++static int ps3strgmngr_read_device(u64 bus_index, u64 dev_index, ++ struct ps3strgmngr_device *dev) ++{ ++ struct ps3strgmngr_region *rgn; ++ u64 type, nr_regions, junk; ++ unsigned int rgn_index; ++ int res; ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x7479706500000000ul /* type */, ++ 0, &type, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ switch (type) { ++ case STORAGE_DEV_TYPE_DISK: ++ dev->type = PS3STRGMNGR_DEVTYPE_DISK; ++ break; ++ case STORAGE_DEV_TYPE_CDROM: ++ dev->type = PS3STRGMNGR_DEVTYPE_CDROM; ++ break; ++ case STORAGE_DEV_TYPE_FLASH: ++ dev->type = PS3STRGMNGR_DEVTYPE_FLASH; ++ break; ++ case STORAGE_DEV_TYPE_NOR_FLASH: ++ dev->type = PS3STRGMNGR_DEVTYPE_NOR_FLASH; ++ break; ++ default: ++ return (-ENOTSUPP); ++ break; ++ } ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x6964000000000000ul /* id */, ++ 0, &dev->id, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x626c6b5f73697a65ul /* blk_size */, ++ 0, &dev->block_size, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x6e5f626c6f636b73ul /* n_blocks */, ++ 0, &dev->nr_blocks, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6465760000000000ul | dev_index /* dev */, ++ 0x6e5f726567730000ul /* n_regs */, ++ 0, &nr_regions, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ rgn = dev->region; ++ ++ for (rgn_index = 0; rgn_index < nr_regions; rgn_index++) { ++ res = ps3strgmngr_read_region(bus_index, dev_index, dev->id, type, ++ rgn_index, rgn); ++ if (res) ++ continue; ++ ++ dev->nr_regions++; ++ rgn++; ++ } ++ ++ return (0); ++} ++ ++static int ps3strgmngr_ctl_get_devices(struct ps3strgmngr_ctl_get_devices *get_devices) ++{ ++ struct ps3strgmngr_device *dev; ++ unsigned int bus_index, dev_index; ++ u64 bus_type, bus_id, nr_devices, junk; ++ int res; ++ ++ get_devices->nr_devices = 0; ++ dev = get_devices->device; ++ ++ for (bus_index = 0; bus_index < 10; bus_index++) { ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x7479706500000000ul /* type */, ++ 0, 0, &bus_type, &junk); ++ if (res) ++ continue; ++ ++ if (bus_type == STORAGE_BUS_TYPE) ++ break; ++ } ++ ++ if (bus_index >= 10) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6964000000000000ul /* id */, ++ 0, 0, &bus_id, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ res = lv1_read_repository_node(1, 0x0000000062757300ul | bus_index /* bus */, ++ 0x6e756d5f64657600ul /* num_dev */, ++ 0, 0, &nr_devices, &junk); ++ if (res) ++ return (-ENXIO); ++ ++ for (dev_index = 0; dev_index < nr_devices; dev_index++) { ++ res = ps3strgmngr_read_device(bus_index, dev_index, dev); ++ if (res) ++ continue; ++ ++ get_devices->nr_devices++; ++ dev++; ++ } ++ ++ return (0); ++} ++ ++static int ps3strgmngr_ctl_create_region(struct ps3strgmngr_ctl_create_region *create_region) ++{ ++ u64 tag; ++ int res; ++ ++ res = lv1_storage_create_region(create_region->device_id, create_region->start_block, ++ create_region->nr_blocks, 0, create_region->laid, &create_region->region_id, &tag); ++ if (res) ++ return (-ENXIO); ++ ++ return (0); ++} ++ ++static int ps3strgmngr_ctl_delete_region(struct ps3strgmngr_ctl_delete_region *delete_region) ++{ ++ u64 tag; ++ int res; ++ ++ res = lv1_storage_delete_region(delete_region->device_id, delete_region->region_id, &tag); ++ if (res) ++ return (-ENXIO); ++ ++ return (0); ++} ++ ++static int ps3strgmngr_ctl_set_region_acl_entry(struct ps3strgmngr_ctl_set_region_acl_entry *set_region_acl_entry) ++{ ++ u64 tag; ++ int res; ++ ++ res = lv1_storage_set_region_acl(set_region_acl_entry->device_id, set_region_acl_entry->region_id, ++ set_region_acl_entry->laid, set_region_acl_entry->access_rights, ++ &tag); ++ if (res) ++ return (-ENXIO); ++ ++ return (0); ++} ++ ++static long ps3strgmngr_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ void __user *argp = (void __user *) arg; ++ struct ps3strgmngr_ctl_get_devices *get_devices; ++ struct ps3strgmngr_ctl_create_region *create_region; ++ struct ps3strgmngr_ctl_delete_region *delete_region; ++ struct ps3strgmngr_ctl_set_region_acl_entry *set_region_acl_entry; ++ int res; ++ ++ if (is_compat_task()) ++ argp = compat_ptr(arg); ++ else ++ argp = (void __user *) arg; ++ ++ switch (cmd) { ++ case PS3STRGMNGR_CTL_GET_DEVICES: ++ get_devices = kmalloc(sizeof(*get_devices), GFP_KERNEL); ++ if (!get_devices) ++ return (-ENOMEM); ++ ++ if (copy_from_user(get_devices, argp, sizeof(*get_devices))) { ++ kfree(get_devices); ++ return (-EFAULT); ++ } ++ ++ res = ps3strgmngr_ctl_get_devices(get_devices); ++ if (res) { ++ kfree(get_devices); ++ return (res); ++ } ++ ++ if (copy_to_user(argp, get_devices, sizeof(*get_devices))) { ++ kfree(get_devices); ++ return (-EFAULT); ++ } ++ ++ kfree(get_devices); ++ break; ++ case PS3STRGMNGR_CTL_CREATE_REGION: ++ create_region = kmalloc(sizeof(*create_region), GFP_KERNEL); ++ if (!create_region) ++ return (-ENOMEM); ++ ++ if (copy_from_user(create_region, argp, sizeof(*create_region))) { ++ kfree(create_region); ++ return (-EFAULT); ++ } ++ ++ res = ps3strgmngr_ctl_create_region(create_region); ++ if (res) { ++ kfree(create_region); ++ return (res); ++ } ++ ++ if (copy_to_user(argp, create_region, sizeof(*create_region))) { ++ kfree(create_region); ++ return (-EFAULT); ++ } ++ ++ kfree(create_region); ++ break; ++ case PS3STRGMNGR_CTL_DELETE_REGION: ++ delete_region = kmalloc(sizeof(*delete_region), GFP_KERNEL); ++ if (!delete_region) ++ return (-ENOMEM); ++ ++ if (copy_from_user(delete_region, argp, sizeof(*delete_region))) { ++ kfree(delete_region); ++ return (-EFAULT); ++ } ++ ++ res = ps3strgmngr_ctl_delete_region(delete_region); ++ if (res) { ++ kfree(delete_region); ++ return (res); ++ } ++ ++ if (copy_to_user(argp, delete_region, sizeof(*delete_region))) { ++ kfree(delete_region); ++ return (-EFAULT); ++ } ++ ++ kfree(delete_region); ++ break; ++ case PS3STRGMNGR_CTL_SET_REGION_ACL_ENTRY: ++ set_region_acl_entry = kmalloc(sizeof(*set_region_acl_entry), GFP_KERNEL); ++ if (!set_region_acl_entry) ++ return (-ENOMEM); ++ ++ if (copy_from_user(set_region_acl_entry, argp, sizeof(*set_region_acl_entry))) { ++ kfree(set_region_acl_entry); ++ return (-EFAULT); ++ } ++ ++ res = ps3strgmngr_ctl_set_region_acl_entry(set_region_acl_entry); ++ if (res) { ++ kfree(set_region_acl_entry); ++ return (res); ++ } ++ ++ if (copy_to_user(argp, set_region_acl_entry, sizeof(*set_region_acl_entry))) { ++ kfree(set_region_acl_entry); ++ return (-EFAULT); ++ } ++ ++ kfree(set_region_acl_entry); ++ break; ++ default: ++ return (-ENOIOCTLCMD); ++ break; ++ } ++ ++ return (0); ++} ++ ++static const struct file_operations ps3strgmngr_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ps3strgmngr_ioctl, ++ .compat_ioctl = ps3strgmngr_ioctl, ++}; ++ ++static struct miscdevice ps3strgmngr_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ps3strgmngr", ++ .fops = &ps3strgmngr_fops, ++}; ++ ++static int __init ps3strgmngr_init(void) ++{ ++ int res; ++ ++ res = misc_register(&ps3strgmngr_misc); ++ if (res) { ++ pr_info("%s:%u: misc_register failed %d\n", ++ __func__, __LINE__, res); ++ return (res); ++ } ++ ++ return (0); ++} ++ ++static void __exit ps3strgmngr_exit(void) ++{ ++ misc_deregister(&ps3strgmngr_misc); ++} ++ ++module_init(ps3strgmngr_init); ++module_exit(ps3strgmngr_exit); ++ ++MODULE_AUTHOR("glevand"); ++MODULE_DESCRIPTION("PS3 Storage Manager Driver"); ++MODULE_LICENSE("GPL"); diff --git a/0150-ps3jupiter.patch b/0150-ps3jupiter.patch new file mode 100644 index 0000000..c17c13e --- /dev/null +++ b/0150-ps3jupiter.patch @@ -0,0 +1,4872 @@ +--- a/drivers/net/wireless/Kconfig 2013-07-18 03:19:44.349296942 +0200 ++++ b/drivers/net/wireless/Kconfig 2013-07-18 03:20:20.909299068 +0200 +@@ -281,5 +281,6 @@ + source "drivers/net/wireless/zd1211rw/Kconfig" + source "drivers/net/wireless/mwifiex/Kconfig" + source "drivers/net/wireless/cw1200/Kconfig" ++source "drivers/net/wireless/ps3jupiter/Kconfig" + + endif # WLAN +--- a/drivers/net/wireless/Makefile 2013-07-18 03:19:51.919297381 +0200 ++++ b/drivers/net/wireless/Makefile 2013-07-18 03:20:40.309300197 +0200 +@@ -59,3 +59,5 @@ + obj-$(CONFIG_BRCMSMAC) += brcm80211/ + + obj-$(CONFIG_CW1200) += cw1200/ ++ ++obj-$(CONFIG_PS3_JUPITER) += ps3jupiter/ +--- /dev/null 2012-01-06 10:29:22.673734242 +0100 ++++ b/drivers/net/wireless/ps3jupiter/Kconfig 2012-01-06 12:13:47.507142753 +0100 +@@ -0,0 +1,16 @@ ++ ++config PS3_JUPITER ++ tristate "PS3 Jupiter 802.11bg support" ++ depends on USB ++ ---help--- ++ A driver for the PS3 Jupiter ++ 802.11bg wireless network adapter. ++ ++config PS3_JUPITER_STA ++ tristate "PS3 Jupiter 802.11bg station support" ++ depends on PS3_JUPITER ++ select WIRELESS_EXT ++ select WEXT_PRIV ++ ---help--- ++ A station driver for the PS3 Jupiter ++ 802.11bg wireless network adapter. +--- /dev/null 2012-01-06 10:29:22.673734242 +0100 ++++ b/drivers/net/wireless/ps3jupiter/Makefile 2012-01-06 12:07:05.647715657 +0100 +@@ -0,0 +1,3 @@ ++ ++obj-$(CONFIG_PS3_JUPITER) += ps3_jupiter.o ++obj-$(CONFIG_PS3_JUPITER_STA) += ps3_jupiter_sta.o +--- /dev/null 2012-11-01 00:09:58.397610712 -0800 ++++ b/drivers/net/wireless/ps3jupiter/ps3_eurus.h 2012-11-01 03:22:11.000000000 -0800 +@@ -0,0 +1,605 @@ ++ ++/* ++ * PS3 Eurus ++ * ++ * Copyright (C) 2011 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef _PS3_EURUS_H ++#define _PS3_EURUS_H ++ ++enum ps3_eurus_cmd_id { ++ PS3_EURUS_CMD_0x1 = 0x0001, ++ PS3_EURUS_CMD_GET_AP_SSID = 0x0003, ++ PS3_EURUS_CMD_SET_AP_SSID = 0x0005, ++ PS3_EURUS_CMD_GET_CHANNEL = 0x000f, ++ PS3_EURUS_CMD_SET_CHANNEL = 0x0011, ++ PS3_EURUS_CMD_SET_ANTENNA = 0x0029, ++ PS3_EURUS_CMD_GET_AP_WEP_CONFIG = 0x0059, ++ PS3_EURUS_CMD_SET_AP_WEP_CONFIG = 0x005b, ++ PS3_EURUS_CMD_0x61 = 0x0061, ++ PS3_EURUS_CMD_0x65 = 0x0065, ++ PS3_EURUS_CMD_GET_FW_VERSION = 0x0099, ++ PS3_EURUS_CMD_GET_AP_OPMODE = 0x00b7, ++ PS3_EURUS_CMD_SET_AP_OPMODE = 0x00b9, ++ PS3_EURUS_CMD_0xc5 = 0x00c5, ++ PS3_EURUS_CMD_GET_AP_WPA_AKM_SUITE = 0x00c8, ++ PS3_EURUS_CMD_SET_AP_WPA_AKM_SUITE = 0x00c9, ++ PS3_EURUS_CMD_GET_AP_WPA_GROUP_CIPHER_SUITE = 0x00cd, ++ PS3_EURUS_CMD_SET_AP_WPA_GROUP_CIPHER_SUITE = 0x00cf, ++ PS3_EURUS_CMD_GET_AP_WPA_PSK_PASSPHRASE = 0x00d1, ++ PS3_EURUS_CMD_SET_AP_WPA_PSK_PASSPHRASE = 0x00d3, ++ PS3_EURUS_CMD_0xd5 = 0x00d5, ++ PS3_EURUS_CMD_0x127 = 0x0127, ++ PS3_EURUS_CMD_0x12b = 0x012b, ++ PS3_EURUS_CMD_GET_AP_WPA_PSK_BIN = 0x017b, ++ PS3_EURUS_CMD_SET_AP_WPA_PSK_BIN = 0x017d, ++ PS3_EURUS_CMD_GET_AP_WPA_PAIRWISE_CIPHER_SUITE = 0x01bd, ++ PS3_EURUS_CMD_SET_AP_WPA_PAIRWISE_CIPHER_SUITE = 0x01bf, ++ PS3_EURUS_CMD_0x1d9 = 0x01d9, ++ PS3_EURUS_CMD_START_AP = 0x01dd, ++ PS3_EURUS_CMD_0x1ed = 0x01ed, ++ PS3_EURUS_CMD_GET_HW_REVISION = 0x01fb, ++ PS3_EURUS_CMD_0x203 = 0x0203, ++ PS3_EURUS_CMD_0x207 = 0x0207, ++ PS3_EURUS_CMD_ASSOCIATE = 0x1001, ++ PS3_EURUS_CMD_GET_COMMON_CONFIG = 0x1003, ++ PS3_EURUS_CMD_SET_COMMON_CONFIG = 0x1005, ++ PS3_EURUS_CMD_GET_WEP_CONFIG = 0x1013, ++ PS3_EURUS_CMD_SET_WEP_CONFIG = 0x1015, ++ PS3_EURUS_CMD_GET_WPA_CONFIG = 0x1017, ++ PS3_EURUS_CMD_SET_WPA_CONFIG = 0x1019, ++ PS3_EURUS_CMD_0x1025 = 0x1025, ++ PS3_EURUS_CMD_0x1031 = 0x1031, ++ PS3_EURUS_CMD_GET_SCAN_RESULTS = 0x1033, ++ PS3_EURUS_CMD_START_SCAN = 0x1035, ++ PS3_EURUS_CMD_DISASSOCIATE = 0x1037, ++ PS3_EURUS_CMD_GET_RSSI = 0x103d, ++ PS3_EURUS_CMD_GET_MAC_ADDR = 0x103f, ++ PS3_EURUS_CMD_SET_MAC_ADDR = 0x1041, ++ PS3_EURUS_CMD_0x104d = 0x104d, ++ PS3_EURUS_CMD_0x104f = 0x104f, ++ PS3_EURUS_CMD_0x105f = 0x105f, ++ PS3_EURUS_CMD_0x1109 = 0x1109, ++ PS3_EURUS_CMD_0x110b = 0x110b, ++ PS3_EURUS_CMD_0x110d = 0x110d, ++ PS3_EURUS_CMD_0x1133 = 0x1133, ++ PS3_EURUS_CMD_0x114b = 0x114b, ++ PS3_EURUS_CMD_0x114f = 0x114f, ++ PS3_EURUS_CMD_0x115b = 0x115b, ++ PS3_EURUS_CMD_0x115d = 0x115d, ++ PS3_EURUS_CMD_0x115f = 0x115f, ++ PS3_EURUS_CMD_SET_MCAST_ADDR_FILTER = 0x1161, ++ PS3_EURUS_CMD_CLEAR_MCAST_ADDR_FILTER = 0x1163, ++ PS3_EURUS_CMD_GET_MCAST_ADDR_FILTER = 0x1165, ++ PS3_EURUS_CMD_0x116d = 0x116d, ++ PS3_EURUS_CMD_0x116f = 0x116f, ++ PS3_EURUS_CMD_GET_MAC_ADDR_LIST = 0x1117, ++ PS3_EURUS_CMD_0x1171 = 0x1171, ++ PS3_EURUS_CMD_GET_CHANNEL_INFO = 0xfffd, ++}; ++ ++enum ps3_eurus_cmd_status { ++ PS3_EURUS_CMD_OK = 0x0001, ++ PS3_EURUS_CMD_INVALID_LENGTH = 0x0002, ++ PS3_EURUS_CMD_UNSUPPORTED = 0x0003, ++ PS3_EURUS_CMD_INVALID_PARAMETER = 0x0004, ++}; ++ ++enum ps3_eurus_event_type { ++ PS3_EURUS_EVENT_TYPE_0x8 = 0x00000008, ++ PS3_EURUS_EVENT_TYPE_0x10 = 0x00000010, ++ PS3_EURUS_EVENT_TYPE_0x40 = 0x00000040, ++ PS3_EURUS_EVENT_TYPE_0x80 = 0x00000080, ++ PS3_EURUS_EVENT_TYPE_0x100 = 0x00000100, ++ PS3_EURUS_EVENT_TYPE_0x400 = 0x00000400, ++ PS3_EURUS_EVENT_TYPE_0x80000000 = 0x80000000 ++}; ++ ++enum ps3_eurus_event_id { ++ /* event type 0x00000008 */ ++ ++ PS3_EURUS_EVENT_STA_CONNECTED = 0x00000010, ++ ++ /* event type 0x00000010 */ ++ ++ PS3_EURUS_EVENT_STA_DISCONNECTED = 0x00000002, ++ PS3_EURUS_EVENT_AP_STOPPED = 0x00000004, ++ ++ /* event type 0x00000040 */ ++ ++ PS3_EURUS_EVENT_DEAUTH = 0x00000001, ++ ++ /* event type 0x00000080 */ ++ ++ PS3_EURUS_EVENT_BEACON_LOST = 0x00000001, ++ PS3_EURUS_EVENT_CONNECTED = 0x00000002, ++ PS3_EURUS_EVENT_SCAN_COMPLETED = 0x00000004, ++ PS3_EURUS_EVENT_WPA_CONNECTED = 0x00000020, ++ PS3_EURUS_EVENT_WPA_ERROR = 0x00000040, ++ ++ /* event type 0x00000100 */ ++ ++ PS3_EURUS_EVENT_0x100_0x2 = 0x00000002, ++ PS3_EURUS_EVENT_AP_STARTED = 0x00000010, ++ PS3_EURUS_EVENT_STA_WPA_CONNECTED = 0x00000020, ++ PS3_EURUS_EVENT_0x100_0x40 = 0x00000040, ++ ++ /* event type 0x80000000 */ ++ ++ PS3_EURUS_EVENT_DEVICE_READY = 0x00000001, ++}; ++ ++enum ps3_eurus_ap_opmode { ++ PS3_EURUS_AP_OPMODE_11B = 0x00000000, ++ PS3_EURUS_AP_OPMODE_11G = 0x00000001, ++ PS3_EURUS_AP_OPMODE_11BG = 0x00000002, ++}; ++ ++enum ps3_eurus_bss_type { ++ PS3_EURUS_BSS_INFRA = 0x00, ++ PS3_EURUS_BSS_ADHOC = 0x02, ++}; ++ ++enum ps3_eurus_auth_mode { ++ PS3_EURUS_AUTH_OPEN = 0x00, ++ PS3_EURUS_AUTH_SHARED_KEY = 0x01, ++}; ++ ++enum ps3_eurus_opmode { ++ PS3_EURUS_OPMODE_11BG = 0x00, ++ PS3_EURUS_OPMODE_11B = 0x01, ++ PS3_EURUS_OPMODE_11G = 0x02, ++}; ++ ++enum ps3_eurus_preamble_mode { ++ PS3_EURUS_PREAMBLE_SHORT = 0x00, ++ PS3_EURUS_PREAMBLE_LONG = 0x01, ++}; ++ ++enum ps3_eurus_wep_security_mode { ++ PS3_EURUS_WEP_SECURITY_NONE = 0x00, ++ PS3_EURUS_WEP_SECURITY_40BIT = 0x01, ++ PS3_EURUS_WEP_SECURITY_104BIT = 0x02, ++}; ++ ++enum ps3_eurus_wpa_security_mode { ++ PS3_EURUS_WPA_SECURITY_WPA = 0x00, ++ PS3_EURUS_WPA_SECURITY_WPA2 = 0x01, ++}; ++ ++enum ps3_eurus_wpa_psk_type { ++ PS3_EURUS_WPA_PSK_PASSPHRASE = 0x00, ++ PS3_EURUS_WPA_PSK_BIN = 0x01, ++}; ++ ++enum ps3_eurus_wpa_cipher_suite { ++ PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP = 0x0050f202, ++ PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES = 0x0050f204, ++ PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP = 0x000fac02, ++ PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES = 0x000fac04, ++}; ++ ++enum ps3_eurus_wpa_akm_suite { ++ PS3_EURUS_WPA_AKM_SUITE_WPA_PSK = 0x0050f202, ++ PS3_EURUS_WPA_AKM_SUITE_WPA2_PSK = 0x000fac02, ++}; ++ ++struct ps3_eurus_cmd_hdr { ++ __le16 id; /* enum ps3_eurus_cmd_id */ ++ __le16 tag; ++ __le16 status; /* enum ps3_eurus_cmd_status */ ++ __le16 payload_length; ++ u8 res[4]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1 { ++ u8 unknown; ++ u8 res[3]; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_ssid { ++ u8 ssid[32]; ++ u8 res[4]; ++} __packed; ++ ++struct ps3_eurus_cmd_get_channel { ++ u8 unknown[35]; ++ __le16 channel; ++} __packed; ++ ++struct ps3_eurus_cmd_set_channel { ++ u8 channel; ++} __packed; ++ ++struct ps3_eurus_cmd_set_antenna { ++ u8 unknown1; ++ u8 unknown2; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wep_config { ++ u8 unknown1; ++ u8 unknown2; ++ u8 security_mode; /* enum ps3_eurus_wep_security_mode */ ++ u8 unknown3; ++ u8 key[4][18]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x61 { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x65 { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_get_fw_version { ++ u8 version[62]; /* string */ ++} __packed; ++ ++struct ps3_eurus_cmd_ap_opmode { ++ __le32 opmode; /* enum ps3_eurus_ap_opmode */ ++} __packed; ++ ++struct ps3_eurus_cmd_0xc5 { ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wpa_akm_suite { ++ __be32 suite; /* enum ps3_eurus_wpa_akm_suite */ ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wpa_group_cipher_suite { ++ __be32 cipher_suite; /* enum ps3_eurus_wpa_cipher_suite */ ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wpa_psk_passphrase { ++ u8 passphrase[64]; ++} __packed; ++ ++struct ps3_eurus_cmd_0xd5 { ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x127 { ++ u8 res[4]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x12b { ++ u8 res[4]; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wpa_psk_bin { ++ u8 enable; ++ u8 psk[32]; ++} __packed; ++ ++struct ps3_eurus_cmd_ap_wpa_pairwise_cipher_suite { ++ __be32 cipher_suite; /* enum ps3_eurus_wpa_cipher_suite */ ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1d9 { ++ u8 unknown1; ++ u8 unknown2; ++ u8 res[2]; ++} __packed; ++ ++struct ps3_eurus_cmd_start_ap { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1ed { ++ __le32 unknown1; ++ u8 unknown2; ++ u8 unknown3; ++ u8 unknown4; ++ u8 unknown5; ++ u8 unknown6; ++ u8 unknown7; ++ u8 unknown8; ++} __packed; ++ ++struct ps3_eurus_cmd_get_hw_revision { ++ u8 unknown[4]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x203 { ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x207 { ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_associate { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_common_config { ++ u8 bss_type; /* enum ps3_eurus_bss_type */ ++ u8 auth_mode; /* enum ps3_eurus_auth_mode */ ++ u8 opmode; /* enum ps3_eurus_opmode */ ++ u8 unknown; ++ u8 bssid[6]; ++ __le16 capability; ++ u8 ie[0]; ++} __packed; ++ ++struct ps3_eurus_cmd_wep_config { ++ u8 unknown1; ++ u8 security_mode; /* enum ps3_eurus_wep_security_mode */ ++ __le16 unknown2; ++ u8 key[4][16]; ++} __packed; ++ ++struct ps3_eurus_cmd_wpa_config { ++ u8 unknown; ++ u8 security_mode; /* enum ps3_eurus_wpa_security_mode */ ++ u8 psk_type; /* enum ps3_eurus_wpa_psk_type */ ++ u8 psk[64]; ++ __be32 group_cipher_suite; /* enum ps3_eurus_wpa_cipher_suite */ ++ __be32 pairwise_cipher_suite; /* enum ps3_eurus_wpa_cipher_suite */ ++ __be32 akm_suite; /* enum ps3_eurus_wpa_akm_suite */ ++} __packed; ++ ++struct ps3_eurus_cmd_0x1025 { ++ u8 preamble_mode; /* enum ps3_eurus_preamble_mode */ ++ u8 res[3]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1031 { ++ u8 unknown1; ++ u8 unknown2; ++} __packed; ++ ++struct ps3_eurus_scan_result { ++ __le16 length; ++ u8 bssid[6]; ++ u8 rssi; ++ __le64 timestamp; ++ __le16 beacon_period; /* in msec */ ++ __le16 capability; ++ u8 ie[0]; ++} __packed; ++ ++#define PS3_EURUS_SCAN_RESULTS_MAXSIZE 0x5b0 ++ ++struct ps3_eurus_cmd_get_scan_results { ++ u8 count; ++ struct ps3_eurus_scan_result result[0]; ++} __packed; ++ ++struct ps3_eurus_cmd_start_scan { ++ u8 unknown1; ++ u8 unknown2; ++ __le16 channel_dwell; /* in msec */ ++ u8 res[6]; ++ u8 ie[0]; ++} __packed; ++ ++struct ps3_eurus_cmd_disassociate { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_get_rssi { ++ u8 res[10]; ++ u8 rssi; ++} __packed; ++ ++struct ps3_eurus_cmd_get_mac_addr { ++ u8 unknown; ++ u8 mac_addr[6]; ++} __packed; ++ ++struct ps3_eurus_cmd_set_mac_addr { ++ u8 mac_addr[6]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x104d { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x104f { ++ u8 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x105f { ++ __le16 channel_info; ++ u8 mac_addr[6]; ++ u8 unknown1; ++ u8 unknown2; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1109 { ++ __le16 unknown1; ++ __le16 unknown2; ++ __le16 unknown3; ++ __le16 unknown4; ++ __le16 unknown5; ++ __le16 unknown6; ++ __le16 unknown7; ++ u8 unknown8; ++ u8 res; ++ u8 unknown9; ++ u8 unknown10; ++ __le16 unknown11; ++ __le16 unknown12; ++} __packed; ++ ++struct ps3_eurus_cmd_0x110b { ++ __le32 unknown1; ++ u8 res[4]; ++ __le32 unknown2; ++} __packed; ++ ++struct ps3_eurus_cmd_0x110d { ++ u8 res1[12]; ++ __le32 unknown1; ++ __le32 unknown2; ++ __le32 unknown3; ++ __le32 unknown4; ++ __le32 unknown5; ++ __le32 unknown6; ++ __le32 unknown7; ++ u8 res2[88]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x1133 { ++ u8 unknown1; ++ u8 unknown2; ++ u8 unknown3; ++ u8 unknown4; ++ __le32 unknown5; ++ u8 res[6]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x114f { ++ u8 res[1304]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x115b { ++ __le16 unknown1; ++ __le16 unknown2; ++ u8 mac_addr[6]; ++ u8 res[84]; ++} __packed; ++ ++struct ps3_eurus_cmd_mcast_addr_filter { ++ __le32 word[8]; ++} __packed; ++ ++struct ps3_eurus_cmd_0x116d { ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_cmd_0x116f { ++ __le32 unknown; ++} __packed; ++ ++#define PS3_EURUS_MAC_ADDR_LIST_MAXSIZE 0xc2 ++ ++struct ps3_eurus_cmd_get_mac_addr_list { ++ __le16 count; /* number of MAC addresses */ ++ u8 mac_addr[0]; ++} __packed; ++ ++struct ps3_eurus_cmd_get_channel_info { ++ u16 channel_info; ++} __packed; ++ ++struct ps3_eurus_event_hdr { ++ __le32 type; /* enum ps3_eurus_event_type */ ++ __le32 id; /* enum ps3_eurus_event_id */ ++ __le32 timestamp; ++ __le32 payload_length; ++ __le32 unknown; ++} __packed; ++ ++struct ps3_eurus_event { ++ struct ps3_eurus_event_hdr hdr; ++ u8 payload[44]; ++} __packed; ++ ++/* ++ * ps3_eurus_rssi2percentage ++ */ ++static inline u8 ps3_eurus_rssi2percentage(u8 rssi) ++{ ++ if (rssi > 89) ++ return 1; ++ else if (rssi < 50) ++ return 100; ++ else ++ return ((90 - rssi) * 100) / 40; ++} ++ ++#define PS3_EURUS_MCAST_ADDR_HASH2VAL(h) (1 << ((h) & 0x1f)) ++#define PS3_EURUS_MCAST_ADDR_HASH2POS(h) (((h) >> 5) & 0x7) ++ ++/* ++ * ps3_eurus_mcast_addr_hash ++ */ ++static inline u8 ps3_eurus_mcast_addr_hash(const u8 mac_addr[ETH_ALEN]) ++{ ++ u8 buf[ETH_ALEN]; ++ u32 h; ++ unsigned int i, j; ++ ++ memcpy(buf, mac_addr, ETH_ALEN); ++ ++ /* reverse bits in each byte */ ++ ++ for (i = 0; i < ETH_ALEN; i++) { ++ buf[i] = (buf[i] >> 4) | ((buf[i] & 0xf) << 4); ++ buf[i] = ((buf[i] & 0xcc) >> 2) | ((buf[i] & 0x33) << 2); ++ buf[i] = ((buf[i] & 0xaa) >> 1) | ((buf[i] & 0x55) << 1); ++ } ++ ++ h = 0xffffffff; ++ ++ for (i = 0; i < ETH_ALEN; i++) { ++ h = (((unsigned int) buf[i]) << 24) ^ h; ++ ++ for (j = 0; j < 8; j++) { ++ if (((int) h) >= 0) { ++ h = h << 1; ++ } else { ++ h = (h << 1) ^ 0x4c10000; ++ h = h ^ 0x1db7; ++ } ++ } ++ } ++ ++ h = ((h >> 24) & 0xf8) | (h & 0x7); ++ ++ return (h & 0xff); ++} ++ ++/* ++ * ps3_eurus_make_cmd_0x1109 ++ */ ++static inline void ps3_eurus_make_cmd_0x1109(struct ps3_eurus_cmd_0x1109 *cmd_0x1109, ++ u8 arg1, u16 arg2, u16 arg3, u16 arg4, u16 arg5) ++{ ++ memset(cmd_0x1109, 0, sizeof(*cmd_0x1109)); ++ ++ cmd_0x1109->unknown1 = cpu_to_le16(0x1); ++ cmd_0x1109->unknown8 = arg1; ++ ++ if (arg1 == 0x0) { ++ cmd_0x1109->unknown6 = cpu_to_le16(0xa); ++ } else if (arg1 == 0x1) { ++ cmd_0x1109->unknown2 = cpu_to_le16(arg2); ++ cmd_0x1109->unknown3 = cpu_to_le16(arg3); ++ cmd_0x1109->unknown4 = cpu_to_le16(arg5); ++ ++ if (arg2 == 0x0) ++ cmd_0x1109->unknown5 = cpu_to_le16(0x6); ++ else ++ cmd_0x1109->unknown5 = cpu_to_le16(0x2); ++ ++ cmd_0x1109->unknown7 = cpu_to_le16(arg4); ++ cmd_0x1109->unknown9 = 0xff; ++ cmd_0x1109->unknown10 = 0xff; ++ cmd_0x1109->unknown11 = 0xffff; ++ cmd_0x1109->unknown12 = 0xffff; ++ } ++} ++ ++#endif +--- /dev/null 2012-11-01 00:09:58.397610712 -0800 ++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter.h 2012-11-01 03:22:23.000000000 -0800 +@@ -0,0 +1,34 @@ ++ ++/* ++ * PS3 Jupiter ++ * ++ * Copyright (C) 2011 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef _PS3_JUPITER_H ++#define _PS3_JUPITER_H ++ ++int ps3_jupiter_register_event_listener(struct notifier_block *listener); ++ ++int ps3_jupiter_unregister_event_listener(struct notifier_block *listener); ++ ++int ps3_jupiter_exec_eurus_cmd(enum ps3_eurus_cmd_id cmd, ++ void *payload, unsigned int payload_length, ++ unsigned int *response_status, ++ unsigned int *response_length, void *response); ++ ++#endif +--- /dev/null 2012-11-01 00:09:58.397610712 -0800 ++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter.c 2012-11-01 03:22:27.000000000 -0800 +@@ -0,0 +1,1245 @@ ++ ++/* ++ * PS3 Jupiter ++ * ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "ps3_eurus.h" ++#include "ps3_jupiter.h" ++ ++#define PS3_JUPITER_EP 0x5 ++ ++#define PS3_JUPITER_IRQ_BUFSIZE 2048 ++#define PS3_JUPITER_CMD_BUFSIZE 2048 ++ ++#define LV1_SB_BUS_ID 0x1 ++#define LV1_GELIC_DEV_ID 0x0 ++#define LV1_GET_MAC_ADDRESS 0x1 ++#define LV1_GET_CHANNEL_INFO 0x6 ++ ++enum ps3_jupiter_pkt_type { ++ PS3_JUPITER_PKT_CMD = 6, ++ PS3_JUPITER_PKT_EVENT = 8, ++}; ++ ++struct ps3_jupiter_dev { ++ struct usb_device *udev; ++ struct urb *irq_urb, *cmd_urb; ++ void *irq_buf, *cmd_buf; ++ ++ u16 cmd_tag, eurus_cmd, eurus_tag; ++ struct completion cmd_done_comp; ++ spinlock_t cmd_lock; ++ int cmd_busy, cmd_err; ++ ++ struct workqueue_struct *event_queue; ++ struct delayed_work event_work; ++ struct blocking_notifier_head event_listeners; ++ struct list_head event_list; ++ spinlock_t event_list_lock; ++ ++ struct notifier_block event_listener; ++ struct completion event_comp; ++ ++ unsigned char mac_addr[ETH_ALEN]; ++ u64 channel_info; ++ ++ u16 dev_status; ++ int dev_ready; ++}; ++ ++struct ps3_jupiter_pkt_hdr { ++ u8 unknown1; ++ u8 unknown2; ++ u8 type; ++} __packed; ++ ++struct ps3_jupiter_cmd_hdr { ++ u8 unknown1; ++ __le16 unknown2; ++ u8 res1[2]; ++ __le16 tag; ++ u8 res2[14]; ++} __packed; ++ ++struct ps3_jupiter_event_hdr { ++ u8 count; ++} __packed; ++ ++struct ps3_jupiter_list_event { ++ struct list_head list; ++ struct ps3_eurus_event event; ++}; ++ ++static struct ps3_jupiter_dev *ps3jd; ++ ++static unsigned char ps3_jupiter_devkey[] = { ++ 0x76, 0x4e, 0x4b, 0x07, 0x24, 0x42, 0x53, 0xfb, 0x5a, 0xc7, 0xcc, 0x1d, 0xae, 0x00, 0xc6, 0xd8, ++ 0x14, 0x40, 0x61, 0x8b, 0x13, 0x17, 0x4d, 0x7c, 0x3b, 0xb6, 0x90, 0xb8, 0x6e, 0x8b, 0xbb, 0x1d, ++}; ++ ++/* ++ * ps3_jupiter_event_worker ++ */ ++static void ps3_jupiter_event_worker(struct work_struct *work) ++{ ++ struct ps3_jupiter_dev *jd = container_of(work, struct ps3_jupiter_dev, event_work.work); ++ struct ps3_jupiter_list_event *list_event; ++ unsigned long flags; ++ ++ /* dispatch received events to each listener */ ++ ++ while (1) { ++ spin_lock_irqsave(&jd->event_list_lock, flags); ++ ++ if (list_empty(&jd->event_list)) { ++ spin_unlock_irqrestore(&jd->event_list_lock, flags); ++ break; ++ } ++ ++ list_event = list_entry(jd->event_list.next, struct ps3_jupiter_list_event, list); ++ list_del(&list_event->list); ++ ++ spin_unlock_irqrestore(&jd->event_list_lock, flags); ++ ++ blocking_notifier_call_chain(&jd->event_listeners, 0, &list_event->event); ++ ++ kfree(list_event); ++ } ++} ++ ++/* ++ * ps3_jupiter_event_irq ++ */ ++static void ps3_jupiter_event_irq(struct ps3_jupiter_dev *jd, ++ void *buf, unsigned int length) ++{ ++ struct usb_device *udev = jd->udev; ++ struct ps3_jupiter_pkt_hdr *pkt_hdr; ++ struct ps3_jupiter_event_hdr *event_hdr; ++ struct ps3_jupiter_list_event *list_event; ++ unsigned long flags; ++ int i; ++ ++ dev_dbg(&udev->dev, "got event IRQ packet\n"); ++ ++ if (length < sizeof(*pkt_hdr) + sizeof(*event_hdr)) { ++ dev_err(&udev->dev, "got event IRQ packet with invalid length (%d)\n", ++ length); ++ return; ++ } ++ ++ pkt_hdr = (struct ps3_jupiter_pkt_hdr *) buf; ++ event_hdr = (struct ps3_jupiter_event_hdr *) (pkt_hdr + 1); ++ ++ if (length < sizeof(*pkt_hdr) + sizeof(*event_hdr) + ++ event_hdr->count * sizeof(struct ps3_eurus_event)) { ++ dev_err(&udev->dev, "got event IRQ packet with invalid length (%d)\n", ++ length); ++ return; ++ } ++ ++ dev_dbg(&udev->dev, "got %d event(s)\n", event_hdr->count); ++ ++ for (i = 0; i < event_hdr->count; i++) { ++ list_event = kmalloc(sizeof(*list_event), GFP_ATOMIC); ++ if (!list_event) { ++ dev_err(&udev->dev, "could not allocate memory for new event\n"); ++ continue; ++ } ++ ++ memcpy(&list_event->event, (unsigned char *) event_hdr + sizeof(*event_hdr) + ++ i * sizeof(struct ps3_eurus_event), sizeof(struct ps3_eurus_event)); ++ list_event->event.hdr.type = le32_to_cpu(list_event->event.hdr.type); ++ list_event->event.hdr.id = le32_to_cpu(list_event->event.hdr.id); ++ list_event->event.hdr.timestamp = le32_to_cpu(list_event->event.hdr.timestamp); ++ list_event->event.hdr.payload_length = le32_to_cpu(list_event->event.hdr.payload_length); ++ list_event->event.hdr.unknown = le32_to_cpu(list_event->event.hdr.unknown); ++ ++ spin_lock_irqsave(&jd->event_list_lock, flags); ++ list_add_tail(&list_event->list, &jd->event_list); ++ spin_unlock_irqrestore(&jd->event_list_lock, flags); ++ } ++ ++ if (event_hdr->count) ++ queue_delayed_work(jd->event_queue, &jd->event_work, 0); ++} ++ ++/* ++ * ps3_jupiter_cmd_irq ++ */ ++static void ps3_jupiter_cmd_irq(struct ps3_jupiter_dev *jd, ++ void *buf, unsigned int length) ++{ ++ struct usb_device *udev = jd->udev; ++ struct ps3_jupiter_pkt_hdr *pkt_hdr; ++ struct ps3_jupiter_cmd_hdr *cmd_hdr; ++ struct ps3_eurus_cmd_hdr *eurus_cmd_hdr; ++ u16 cmd_tag, eurus_cmd, eurus_tag, payload_length; ++ ++ dev_dbg(&udev->dev, "got command IRQ packet\n"); ++ ++ if (length < sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr)) { ++ dev_err(&udev->dev, "got command IRQ packet with invalid length (%d)\n", ++ length); ++ return; ++ } ++ ++ pkt_hdr = (struct ps3_jupiter_pkt_hdr *) buf; ++ cmd_hdr = (struct ps3_jupiter_cmd_hdr *) (pkt_hdr + 1); ++ eurus_cmd_hdr = (struct ps3_eurus_cmd_hdr *) (cmd_hdr + 1); ++ payload_length = le16_to_cpu(eurus_cmd_hdr->payload_length); ++ ++ if (length < sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr) + payload_length) { ++ dev_err(&udev->dev, "got command IRQ packet with invalid length (%d)\n", ++ length); ++ return; ++ } ++ ++ cmd_tag = le16_to_cpu(cmd_hdr->tag); ++ ++ if (jd->cmd_tag != cmd_tag) ++ dev_err(&udev->dev, "got command IRQ packet with invalid command tag, " ++ "got (0x%04x), expected (0x%04x)\n", cmd_tag, jd->cmd_tag); ++ ++ eurus_cmd = le16_to_cpu(eurus_cmd_hdr->id); ++ ++ if ((jd->eurus_cmd + 1) != eurus_cmd) ++ dev_err(&udev->dev, "got command IRQ packet with invalid EURUS command, " ++ "got (0x%04x), expected (0x%04x)\n", eurus_cmd, jd->eurus_cmd); ++ ++ eurus_tag = le16_to_cpu(eurus_cmd_hdr->tag); ++ ++ if (jd->eurus_tag != eurus_tag) ++ dev_err(&udev->dev, "got command IRQ packet with invalid EURUS tag, " ++ "got (0x%04x), expected (0x%04x)\n", eurus_tag, jd->eurus_tag); ++ ++ memcpy(jd->cmd_buf, buf, length); ++ ++ jd->cmd_err = 0; ++ complete(&jd->cmd_done_comp); ++} ++ ++/* ++ * ps3_jupiter_irq_urb_complete ++ */ ++static void ps3_jupiter_irq_urb_complete(struct urb *urb) ++{ ++ struct ps3_jupiter_dev *jd = urb->context; ++ struct usb_device *udev = jd->udev; ++ struct ps3_jupiter_pkt_hdr *pkt_hdr; ++ int err; ++ ++ dev_dbg(&udev->dev, "IRQ URB completed (%d)\n", urb->status); ++ ++ switch (urb->status) { ++ case 0: ++ if (urb->actual_length < sizeof(*pkt_hdr)) { ++ dev_err(&udev->dev, "got IRQ packet with invalid length (%d)\n", ++ urb->actual_length); ++ break; ++ } ++ ++ pkt_hdr = (struct ps3_jupiter_pkt_hdr *) jd->irq_buf; ++ ++ switch (pkt_hdr->type) { ++ case PS3_JUPITER_PKT_CMD: ++ ps3_jupiter_cmd_irq(jd, pkt_hdr, urb->actual_length); ++ break; ++ case PS3_JUPITER_PKT_EVENT: ++ ps3_jupiter_event_irq(jd, pkt_hdr, urb->actual_length); ++ break; ++ default: ++ dev_err(&udev->dev, "got unknown IRQ packet type (%d)\n", ++ pkt_hdr->type); ++ } ++ break; ++ case -EINPROGRESS: ++ /* ignore */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ case -ENODEV: ++ return; ++ default: ++ dev_err(&udev->dev, "IRQ URB failed (%d)\n", urb->status); ++ } ++ ++ err = usb_submit_urb(jd->irq_urb, GFP_ATOMIC); ++ if (err) ++ dev_err(&udev->dev, "could not submit IRQ URB (%d)\n", err); ++} ++ ++/* ++ * ps3_jupiter_cmd_urb_complete ++ */ ++static void ps3_jupiter_cmd_urb_complete(struct urb *urb) ++{ ++ struct ps3_jupiter_dev *jd = urb->context; ++ struct usb_device *udev = jd->udev; ++ ++ dev_dbg(&udev->dev, "command URB completed (%d)\n", urb->status); ++ ++ switch (urb->status) { ++ case 0: ++ /* success */ ++ break; ++ case -EINPROGRESS: ++ /* ignore */ ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ case -ENODEV: ++ default: ++ dev_err(&udev->dev, "command URB failed (%d)\n", urb->status); ++ jd->cmd_err = urb->status; ++ complete(&jd->cmd_done_comp); ++ } ++} ++ ++/* ++ * _ps3_jupiter_register_event_listener ++ */ ++static int _ps3_jupiter_register_event_listener(struct ps3_jupiter_dev *jd, ++ struct notifier_block *listener) ++{ ++ BUG_ON(!jd); ++ ++ return blocking_notifier_chain_register(&jd->event_listeners, listener); ++} ++ ++/* ++ * ps3_jupiter_register_event_listener ++ */ ++int ps3_jupiter_register_event_listener(struct notifier_block *listener) ++{ ++ struct ps3_jupiter_dev *jd = ps3jd; ++ int err; ++ ++ if (!jd || !jd->dev_ready) ++ return -ENODEV; ++ ++ err = _ps3_jupiter_register_event_listener(jd, listener); ++ ++ return err; ++} ++ ++EXPORT_SYMBOL_GPL(ps3_jupiter_register_event_listener); ++ ++/* ++ * _ps3_jupiter_unregister_event_listener ++ */ ++static int _ps3_jupiter_unregister_event_listener(struct ps3_jupiter_dev *jd, ++ struct notifier_block *listener) ++{ ++ BUG_ON(!jd); ++ ++ return blocking_notifier_chain_unregister(&jd->event_listeners, listener); ++} ++ ++/* ++ * ps3_jupiter_unregister_event_listener ++ */ ++int ps3_jupiter_unregister_event_listener(struct notifier_block *listener) ++{ ++ struct ps3_jupiter_dev *jd = ps3jd; ++ int err; ++ ++ if (!jd || !jd->dev_ready) ++ return -ENODEV; ++ ++ err = _ps3_jupiter_unregister_event_listener(jd, listener); ++ ++ return err; ++} ++ ++EXPORT_SYMBOL_GPL(ps3_jupiter_unregister_event_listener); ++ ++/* ++ * _ps3_jupiter_exec_eurus_cmd ++ */ ++static int _ps3_jupiter_exec_eurus_cmd(struct ps3_jupiter_dev *jd, ++ enum ps3_eurus_cmd_id cmd, ++ void *payload, unsigned int payload_length, ++ unsigned int *response_status, ++ unsigned int *response_length, void *response) ++{ ++ struct usb_device *udev = jd->udev; ++ struct ps3_jupiter_pkt_hdr *pkt_hdr; ++ struct ps3_jupiter_cmd_hdr *cmd_hdr; ++ struct ps3_eurus_cmd_hdr *eurus_cmd_hdr; ++ struct ps3_eurus_cmd_get_channel_info *eurus_cmd_get_channel_info; ++ u16 status; ++ unsigned long flags; ++ int err; ++ ++ BUG_ON(!jd); ++ ++ if (!payload && payload_length) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jd->cmd_lock, flags); ++ ++ if (jd->cmd_busy) { ++ spin_unlock_irqrestore(&jd->cmd_lock, flags); ++ dev_dbg(&udev->dev, ++ "trying to execute multiple commands at the same time\n"); ++ return -EAGAIN; ++ } ++ ++ jd->cmd_busy = 1; ++ ++ spin_unlock_irqrestore(&jd->cmd_lock, flags); ++ ++ dev_dbg(&udev->dev, "EURUS command 0x%04x payload length %d\n", ++ cmd, payload_length); ++ ++ /* internal commands */ ++ ++ if (cmd == PS3_EURUS_CMD_GET_CHANNEL_INFO) { ++ if (payload_length < sizeof(*eurus_cmd_get_channel_info)) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ if (response_status) ++ *response_status = PS3_EURUS_CMD_OK; ++ ++ if (response_length && response) { ++ *response_length = sizeof(*eurus_cmd_get_channel_info); ++ eurus_cmd_get_channel_info = (struct ps3_eurus_cmd_get_channel_info *) response; ++ memset(eurus_cmd_get_channel_info, 0, sizeof(*eurus_cmd_get_channel_info)); ++ eurus_cmd_get_channel_info->channel_info = jd->channel_info >> 48; ++ } ++ ++ err = 0; ++ ++ goto done; ++ } ++ ++ pkt_hdr = (struct ps3_jupiter_pkt_hdr *) jd->cmd_buf; ++ memset(pkt_hdr, 0, sizeof(*pkt_hdr)); ++ pkt_hdr->unknown1 = 1; ++ pkt_hdr->unknown2 = 1; ++ pkt_hdr->type = PS3_JUPITER_PKT_CMD; ++ ++ cmd_hdr = (struct ps3_jupiter_cmd_hdr *) (pkt_hdr + 1); ++ memset(cmd_hdr, 0, sizeof(*cmd_hdr)); ++ jd->cmd_tag++; ++ cmd_hdr->unknown1 = 0; ++ cmd_hdr->unknown2 = cpu_to_le16(1); ++ cmd_hdr->tag = cpu_to_le16(jd->cmd_tag); ++ ++ eurus_cmd_hdr = (struct ps3_eurus_cmd_hdr *) (cmd_hdr + 1); ++ memset(eurus_cmd_hdr, 0, sizeof(*eurus_cmd_hdr)); ++ jd->eurus_cmd = cmd; ++ eurus_cmd_hdr->id = cpu_to_le16(cmd); ++ jd->eurus_tag++; ++ eurus_cmd_hdr->tag = cpu_to_le16(jd->eurus_tag); ++ eurus_cmd_hdr->status = cpu_to_le16(0xa); ++ eurus_cmd_hdr->payload_length = cpu_to_le16(payload_length); ++ ++ if (payload_length) ++ memcpy(eurus_cmd_hdr + 1, payload, payload_length); ++ ++ init_completion(&jd->cmd_done_comp); ++ ++ usb_fill_int_urb(jd->cmd_urb, udev, usb_sndintpipe(udev, PS3_JUPITER_EP), ++ jd->cmd_buf, sizeof(*pkt_hdr) + sizeof(*cmd_hdr) + sizeof(*eurus_cmd_hdr) + payload_length, ++ ps3_jupiter_cmd_urb_complete, jd, 1); ++ ++ err = usb_submit_urb(jd->cmd_urb, GFP_KERNEL); ++ if (err) { ++ dev_err(&udev->dev, "could not submit command URB (%d)\n", err); ++ goto done; ++ } ++ ++ err = wait_for_completion_timeout(&jd->cmd_done_comp, HZ); ++ if (!err) { ++ err = -ETIMEDOUT; ++ goto done; ++ } ++ ++ err = jd->cmd_err; ++ if (!err) { ++ status = le16_to_cpu(eurus_cmd_hdr->status); ++ ++ if (response_status) ++ *response_status = status; ++ ++ if (response_length && response) { ++ *response_length = le16_to_cpu(eurus_cmd_hdr->payload_length); ++ memcpy(response, eurus_cmd_hdr + 1, *response_length); ++ } ++ ++ if (status != PS3_EURUS_CMD_OK) ++ dev_err(&udev->dev, "EURUS command 0x%04x status (0x%04x)\n", cmd, status); ++ } ++ ++done: ++ ++ if (err) ++ dev_err(&udev->dev, "EURUS command 0x%04x failed (%d)\n", cmd, err); ++ ++ jd->cmd_busy = 0; ++ ++ return err; ++} ++ ++/* ++ * _ps3_jupiter_exec_eurus_cmd ++ */ ++int ps3_jupiter_exec_eurus_cmd(enum ps3_eurus_cmd_id cmd, ++ void *payload, unsigned int payload_length, ++ unsigned int *response_status, ++ unsigned int *response_length, void *response) ++{ ++ struct ps3_jupiter_dev *jd = ps3jd; ++ int err; ++ ++ if (!jd || !jd->dev_ready) ++ return -ENODEV; ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, cmd, payload, payload_length, ++ response_status, response_length, response); ++ ++ return err; ++} ++ ++EXPORT_SYMBOL_GPL(ps3_jupiter_exec_eurus_cmd); ++ ++/* ++ * ps3_jupiter_create_event_worker ++ */ ++static int ps3_jupiter_create_event_worker(struct ps3_jupiter_dev *jd) ++{ ++ jd->event_queue = create_singlethread_workqueue("ps3_jupiter_event"); ++ if (!jd->event_queue) ++ return -ENOMEM; ++ ++ INIT_DELAYED_WORK(&jd->event_work, ps3_jupiter_event_worker); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_destroy_event_worker ++ */ ++static void ps3_jupiter_destroy_event_worker(struct ps3_jupiter_dev *jd) ++{ ++ if (jd->event_queue) { ++ cancel_delayed_work(&jd->event_work); ++ flush_workqueue(jd->event_queue); ++ destroy_workqueue(jd->event_queue); ++ jd->event_queue = NULL; ++ } ++} ++ ++/* ++ * ps3_jupiter_free_event_list ++ */ ++static void ps3_jupiter_free_event_list(struct ps3_jupiter_dev *jd) ++{ ++ struct ps3_jupiter_list_event *event, *tmp; ++ ++ list_for_each_entry_safe(event, tmp, &jd->event_list, list) { ++ list_del(&event->list); ++ kfree(event); ++ } ++} ++ ++/* ++ * ps3_jupiter_alloc_urbs ++ */ ++static int ps3_jupiter_alloc_urbs(struct ps3_jupiter_dev *jd) ++{ ++ struct usb_device *udev = jd->udev; ++ ++ jd->irq_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!jd->irq_urb) ++ return -ENOMEM; ++ ++ jd->irq_buf = usb_alloc_coherent(udev, PS3_JUPITER_IRQ_BUFSIZE, ++ GFP_KERNEL, &jd->irq_urb->transfer_dma); ++ if (!jd->irq_buf) ++ return -ENOMEM; ++ ++ usb_fill_int_urb(jd->irq_urb, udev, usb_rcvintpipe(udev, PS3_JUPITER_EP), ++ jd->irq_buf, PS3_JUPITER_IRQ_BUFSIZE, ps3_jupiter_irq_urb_complete, jd, 1); ++ jd->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ jd->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!jd->cmd_urb) ++ return -ENOMEM; ++ ++ jd->cmd_buf = usb_alloc_coherent(udev, PS3_JUPITER_CMD_BUFSIZE, ++ GFP_KERNEL, &jd->cmd_urb->transfer_dma); ++ if (!jd->cmd_buf) ++ return -ENOMEM; ++ ++ usb_fill_int_urb(jd->cmd_urb, udev, usb_sndintpipe(udev, PS3_JUPITER_EP), ++ jd->cmd_buf, PS3_JUPITER_CMD_BUFSIZE, ps3_jupiter_cmd_urb_complete, jd, 1); ++ jd->cmd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_free_urbs ++ */ ++static void ps3_jupiter_free_urbs(struct ps3_jupiter_dev *jd) ++{ ++ struct usb_device *udev = jd->udev; ++ ++ if (jd->irq_urb) { ++ usb_kill_urb(jd->irq_urb); ++ ++ if (jd->irq_buf) ++ usb_free_coherent(udev, PS3_JUPITER_IRQ_BUFSIZE, ++ jd->irq_buf, jd->irq_urb->transfer_dma); ++ ++ usb_free_urb(jd->irq_urb); ++ } ++ ++ if (jd->cmd_urb) { ++ usb_kill_urb(jd->cmd_urb); ++ ++ if (jd->cmd_buf) ++ usb_free_coherent(udev, PS3_JUPITER_CMD_BUFSIZE, ++ jd->cmd_buf, jd->cmd_urb->transfer_dma); ++ ++ usb_free_urb(jd->cmd_urb); ++ } ++} ++ ++/* ++ * ps3_jupiter_dev_auth ++ */ ++static int ps3_jupiter_dev_auth(struct ps3_jupiter_dev *jd) ++{ ++ struct usb_device *udev = jd->udev; ++ void *buf; ++ int err; ++ ++ buf = kmalloc(sizeof(ps3_jupiter_devkey), GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ memcpy(buf, ps3_jupiter_devkey, sizeof(ps3_jupiter_devkey)); ++ ++ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ 0x1, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, 0x9, 0x0, ++ buf, sizeof(ps3_jupiter_devkey), USB_CTRL_SET_TIMEOUT); ++ if (err < 0) { ++ dev_dbg(&udev->dev, "could not send device key (%d)\n", err); ++ return err; ++ } ++ ++ kfree(buf); ++ ++ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), ++ 0x0, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0x2, 0x0, ++ &jd->dev_status, sizeof(jd->dev_status), USB_CTRL_GET_TIMEOUT); ++ if (err < 0) { ++ dev_dbg(&udev->dev, "could not read device status (%d)\n", err); ++ return err; ++ } ++ ++ dev_info(&udev->dev, "device status (0x%04x)\n", jd->dev_status); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_event_handler ++ */ ++static int ps3_jupiter_event_handler(struct notifier_block *n, ++ unsigned long val, void *v) ++{ ++ struct ps3_jupiter_dev *jd = container_of(n, struct ps3_jupiter_dev, event_listener); ++ struct usb_device *udev = jd->udev; ++ struct ps3_eurus_event *event = v; ++ ++ dev_dbg(&udev->dev, "got event (0x%08x 0x%08x 0x%08x 0x%08x 0x%08x)\n", ++ event->hdr.type, event->hdr.id, event->hdr.timestamp, event->hdr.payload_length, ++ event->hdr.unknown); ++ ++ if (event->hdr.type == PS3_EURUS_EVENT_TYPE_0x400) { ++ if ((event->hdr.id == 0x8) || (event->hdr.id == 0x10)) ++ complete(&jd->event_comp); ++ } ++ ++ return NOTIFY_OK; ++} ++ ++/* ++ * ps3_jupiter_dev_init ++ */ ++static int ps3_jupiter_dev_init(struct ps3_jupiter_dev *jd) ++{ ++ struct usb_device *udev = jd->udev; ++ struct ps3_eurus_cmd_0x114f *eurus_cmd_0x114f; ++ struct ps3_eurus_cmd_0x116f *eurus_cmd_0x116f; ++ struct ps3_eurus_cmd_0x115b *eurus_cmd_0x115b; ++ const u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ u8 h; ++ struct ps3_eurus_cmd_mcast_addr_filter *eurus_cmd_mcast_addr_filter; ++ struct ps3_eurus_cmd_0x110d *eurus_cmd_0x110d; ++ struct ps3_eurus_cmd_0x1031 *eurus_cmd_0x1031; ++ struct ps3_eurus_cmd_set_mac_addr *eurus_cmd_set_mac_addr; ++ struct ps3_eurus_cmd_set_antenna *eurus_cmd_set_antenna; ++ struct ps3_eurus_cmd_0x110b *eurus_cmd_0x110b; ++ struct ps3_eurus_cmd_0x1109 *eurus_cmd_0x1109; ++ struct ps3_eurus_cmd_0x207 *eurus_cmd_0x207; ++ struct ps3_eurus_cmd_0x203 *eurus_cmd_0x203; ++ struct ps3_eurus_cmd_0x105f *eurus_cmd_0x105f; ++ struct ps3_eurus_cmd_get_fw_version *eurus_cmd_get_fw_version; ++ unsigned char *buf; ++ unsigned int status, response_length; ++ int err; ++ ++ buf = kmalloc(PS3_JUPITER_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ /* state 1 */ ++ ++ eurus_cmd_0x114f = (struct ps3_eurus_cmd_0x114f *) buf; ++ memset(eurus_cmd_0x114f, 0, sizeof(*eurus_cmd_0x114f)); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x114f, ++ eurus_cmd_0x114f, sizeof(*eurus_cmd_0x114f), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ /* do not check command status here !!! */ ++ ++ /* state 2 */ ++ ++ init_completion(&jd->event_comp); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1171, NULL, 0, &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 3 */ ++ ++ err = wait_for_completion_timeout(&jd->event_comp, HZ); ++ if (!err) { ++ err = -ETIMEDOUT; ++ goto done; ++ } ++ ++ /* state 4 */ ++ ++ eurus_cmd_0x116f = (struct ps3_eurus_cmd_0x116f *) buf; ++ memset(eurus_cmd_0x116f, 0, sizeof(*eurus_cmd_0x116f)); ++ eurus_cmd_0x116f->unknown = cpu_to_le32(0x1); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x116f, ++ eurus_cmd_0x116f, sizeof(*eurus_cmd_0x116f), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 5 */ ++ ++ eurus_cmd_0x115b = (struct ps3_eurus_cmd_0x115b *) buf; ++ memset(eurus_cmd_0x115b, 0, sizeof(*eurus_cmd_0x115b)); ++ eurus_cmd_0x115b->unknown1 = cpu_to_le16(0x1); ++ eurus_cmd_0x115b->unknown2 = cpu_to_le16(0x0); ++ memcpy(eurus_cmd_0x115b->mac_addr, jd->mac_addr, sizeof(jd->mac_addr)); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x115b, ++ eurus_cmd_0x115b, sizeof(*eurus_cmd_0x115b), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 6 */ ++ ++ h = ps3_eurus_mcast_addr_hash(bcast_addr); ++ ++ eurus_cmd_mcast_addr_filter = (struct ps3_eurus_cmd_mcast_addr_filter *) buf; ++ memset(eurus_cmd_mcast_addr_filter, 0, sizeof(*eurus_cmd_mcast_addr_filter)); ++ eurus_cmd_mcast_addr_filter->word[PS3_EURUS_MCAST_ADDR_HASH2POS(h)] |= ++ cpu_to_le32(PS3_EURUS_MCAST_ADDR_HASH2VAL(h)); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_MCAST_ADDR_FILTER, ++ eurus_cmd_mcast_addr_filter, sizeof(*eurus_cmd_mcast_addr_filter), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 7 */ ++ ++ eurus_cmd_0x110d = (struct ps3_eurus_cmd_0x110d *) buf; ++ memset(eurus_cmd_0x110d, 0, sizeof(*eurus_cmd_0x110d)); ++ eurus_cmd_0x110d->unknown1 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown2 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown3 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown4 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown5 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown6 = cpu_to_le32(0xffffffff); ++ eurus_cmd_0x110d->unknown7 = cpu_to_le32(0xffffffff); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x110d, ++ eurus_cmd_0x110d, sizeof(*eurus_cmd_0x110d), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 8 */ ++ ++ eurus_cmd_0x1031 = (struct ps3_eurus_cmd_0x1031 *) buf; ++ memset(eurus_cmd_0x1031, 0, sizeof(*eurus_cmd_0x1031)); ++ eurus_cmd_0x1031->unknown1 = 0x0; ++ eurus_cmd_0x1031->unknown2 = 0x0; ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1031, ++ eurus_cmd_0x1031, sizeof(*eurus_cmd_0x1031), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 9 */ ++ ++ eurus_cmd_set_mac_addr = (struct ps3_eurus_cmd_set_mac_addr *) buf; ++ memset(eurus_cmd_set_mac_addr, 0, sizeof(*eurus_cmd_set_mac_addr)); ++ memcpy(eurus_cmd_set_mac_addr->mac_addr, jd->mac_addr, sizeof(jd->mac_addr)); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_MAC_ADDR, ++ eurus_cmd_set_mac_addr, sizeof(*eurus_cmd_set_mac_addr), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 10 */ ++ ++ eurus_cmd_set_antenna = (struct ps3_eurus_cmd_set_antenna *) buf; ++ memset(eurus_cmd_set_antenna, 0, sizeof(*eurus_cmd_set_antenna)); ++ ++ if (((jd->channel_info >> 40) & 0xff) == 0x1) { ++ eurus_cmd_set_antenna->unknown1 = 0x1; ++ eurus_cmd_set_antenna->unknown2 = 0x0; ++ } else if (((jd->channel_info >> 40) & 0xff) == 0x2) { ++ eurus_cmd_set_antenna->unknown1 = 0x1; ++ eurus_cmd_set_antenna->unknown2 = 0x1; ++ } else { ++ eurus_cmd_set_antenna->unknown1 = 0x2; ++ eurus_cmd_set_antenna->unknown2 = 0x2; ++ } ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_SET_ANTENNA, ++ eurus_cmd_set_antenna, sizeof(*eurus_cmd_set_antenna), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 11 */ ++ ++ eurus_cmd_0x110b = (struct ps3_eurus_cmd_0x110b *) buf; ++ memset(eurus_cmd_0x110b, 0, sizeof(*eurus_cmd_0x110b)); ++ eurus_cmd_0x110b->unknown1 = cpu_to_le32(0x1); ++ eurus_cmd_0x110b->unknown2 = cpu_to_le32(0x200000); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x110b, ++ eurus_cmd_0x110b, sizeof(*eurus_cmd_0x110b), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 12 */ ++ ++ eurus_cmd_0x1109 = (struct ps3_eurus_cmd_0x1109 *) buf; ++ ps3_eurus_make_cmd_0x1109(eurus_cmd_0x1109, 0x1, 0x0, 0x2715, 0x9, 0x12); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x1109, ++ eurus_cmd_0x1109, sizeof(*eurus_cmd_0x1109), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 13 */ ++ ++ eurus_cmd_0x207 = (struct ps3_eurus_cmd_0x207 *) buf; ++ memset(eurus_cmd_0x207, 0, sizeof(*eurus_cmd_0x207)); ++ eurus_cmd_0x207->unknown = cpu_to_le32(0x1); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x207, ++ eurus_cmd_0x207, sizeof(*eurus_cmd_0x207), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 14 */ ++ ++ eurus_cmd_0x203 = (struct ps3_eurus_cmd_0x203 *) buf; ++ memset(eurus_cmd_0x203, 0, sizeof(*eurus_cmd_0x203)); ++ eurus_cmd_0x203->unknown = cpu_to_le32(0x1); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x203, ++ eurus_cmd_0x203, sizeof(*eurus_cmd_0x203), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* state 15 */ ++ ++ eurus_cmd_0x105f = (struct ps3_eurus_cmd_0x105f *) buf; ++ memset(eurus_cmd_0x105f, 0, sizeof(*eurus_cmd_0x105f)); ++ eurus_cmd_0x105f->channel_info = cpu_to_le16(jd->channel_info >> 48); ++ memcpy(eurus_cmd_0x105f->mac_addr, jd->mac_addr, sizeof(jd->mac_addr)); ++ ++ if (((jd->channel_info >> 40) & 0xff) == 0x1) { ++ eurus_cmd_0x105f->unknown1 = 0x1; ++ eurus_cmd_0x105f->unknown2 = 0x0; ++ } else if (((jd->channel_info >> 40) & 0xff) == 0x2) { ++ eurus_cmd_0x105f->unknown1 = 0x1; ++ eurus_cmd_0x105f->unknown2 = 0x1; ++ } else { ++ eurus_cmd_0x105f->unknown1 = 0x2; ++ eurus_cmd_0x105f->unknown2 = 0x2; ++ } ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_0x105f, ++ eurus_cmd_0x105f, sizeof(*eurus_cmd_0x105f), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* device is ready now */ ++ ++ /* read firmware version */ ++ ++ eurus_cmd_get_fw_version = (struct ps3_eurus_cmd_get_fw_version *) buf; ++ memset(eurus_cmd_get_fw_version, 0, sizeof(*eurus_cmd_get_fw_version)); ++ ++ err = _ps3_jupiter_exec_eurus_cmd(jd, PS3_EURUS_CMD_GET_FW_VERSION, ++ eurus_cmd_get_fw_version, sizeof(*eurus_cmd_get_fw_version), ++ &status, &response_length, eurus_cmd_get_fw_version); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ dev_info(&udev->dev, "firmware version: %s\n", (char *) eurus_cmd_get_fw_version->version); ++ ++ err = 0; ++ ++done: ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_probe ++ */ ++static int ps3_jupiter_probe(struct usb_interface *interface, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(interface); ++ struct ps3_jupiter_dev *jd; ++ u64 v1, v2; ++ int err; ++ ++ if (ps3jd) { ++ dev_err(&udev->dev, "only one device is supported\n"); ++ return -EBUSY; ++ } ++ ++ ps3jd = jd = kzalloc(sizeof(struct ps3_jupiter_dev), GFP_KERNEL); ++ if (!jd) ++ return -ENOMEM; ++ ++ jd->udev = usb_get_dev(udev); ++ usb_set_intfdata(interface, jd); ++ ++ spin_lock_init(&jd->cmd_lock); ++ ++ BLOCKING_INIT_NOTIFIER_HEAD(&jd->event_listeners); ++ INIT_LIST_HEAD(&jd->event_list); ++ spin_lock_init(&jd->event_list_lock); ++ ++ init_completion(&jd->event_comp); ++ ++ jd->event_listener.notifier_call = ps3_jupiter_event_handler; ++ ++ err = _ps3_jupiter_register_event_listener(jd, &jd->event_listener); ++ if (err) { ++ dev_err(&udev->dev, "could not register event listener (%d)\n", err); ++ goto fail; ++ } ++ ++ err = ps3_jupiter_create_event_worker(jd); ++ if (err) { ++ dev_err(&udev->dev, "could not create event work queue (%d)\n", err); ++ goto fail; ++ } ++ ++ err = ps3_jupiter_alloc_urbs(jd); ++ if (err) { ++ dev_err(&udev->dev, "could not allocate URBs (%d)\n", err); ++ goto fail; ++ } ++ ++ err = usb_submit_urb(jd->irq_urb, GFP_KERNEL); ++ if (err) { ++ dev_err(&udev->dev, "could not submit IRQ URB (%d)\n", err); ++ goto fail; ++ } ++ ++ err = ps3_jupiter_dev_auth(jd); ++ if (err) { ++ dev_err(&udev->dev, "could not authenticate device (%d)\n", err); ++ goto fail; ++ } ++ ++ /* get MAC address */ ++ ++ err = lv1_net_control(LV1_SB_BUS_ID, LV1_GELIC_DEV_ID, ++ LV1_GET_MAC_ADDRESS, 0, 0, 0, &v1, &v2); ++ if (err) { ++ dev_err(&udev->dev, "could not get MAC address (%d)\n", err); ++ err = -ENODEV; ++ goto fail; ++ } ++ ++ v1 <<= 16; ++ ++ if (!is_valid_ether_addr((unsigned char *) &v1)) { ++ dev_err(&udev->dev, "got invalid MAC address\n"); ++ err = -ENODEV; ++ goto fail; ++ } ++ ++ memcpy(jd->mac_addr, &v1, ETH_ALEN); ++ ++ dev_info(&udev->dev, "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", ++ jd->mac_addr[0], jd->mac_addr[1], jd->mac_addr[2], ++ jd->mac_addr[3], jd->mac_addr[4], jd->mac_addr[5]); ++ ++ /* get channel info */ ++ ++ err = lv1_net_control(LV1_SB_BUS_ID, LV1_GELIC_DEV_ID, ++ LV1_GET_CHANNEL_INFO, 0, 0, 0, &v1, &v2); ++ if (err) { ++ /* don't quit here and try to recover later */ ++ dev_err(&udev->dev, "could not get channel info (%d)\n", err); ++ } ++ ++ if (err) ++ jd->channel_info = (0x7ffull << 48); ++ else ++ jd->channel_info = v1; ++ ++ dev_info(&udev->dev, "channel info: %016llx\n", jd->channel_info); ++ ++ err = ps3_jupiter_dev_init(jd); ++ if (err) { ++ dev_err(&udev->dev, "could not initialize device (%d)\n", err); ++ goto fail; ++ } ++ ++ jd->dev_ready = 1; ++ ++ return 0; ++ ++fail: ++ ++ ps3_jupiter_free_urbs(jd); ++ ++ ps3_jupiter_destroy_event_worker(jd); ++ ++ ps3_jupiter_free_event_list(jd); ++ ++ usb_set_intfdata(interface, NULL); ++ usb_put_dev(udev); ++ ++ kfree(jd); ++ ps3jd = NULL; ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_disconnect ++ */ ++static void ps3_jupiter_disconnect(struct usb_interface *interface) ++{ ++ struct ps3_jupiter_dev *jd = usb_get_intfdata(interface); ++ struct usb_device *udev = jd->udev; ++ ++ jd->dev_ready = 0; ++ ++ ps3_jupiter_free_urbs(jd); ++ ++ ps3_jupiter_destroy_event_worker(jd); ++ ++ ps3_jupiter_free_event_list(jd); ++ ++ usb_set_intfdata(interface, NULL); ++ usb_put_dev(udev); ++ ++ kfree(jd); ++ ps3jd = NULL; ++} ++ ++#ifdef CONFIG_PM ++/* ++ * ps3_jupiter_suspend ++ */ ++static int ps3_jupiter_suspend(struct usb_interface *interface, pm_message_t state) ++{ ++ /*XXX: implement */ ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_resume ++ */ ++static int ps3_jupiter_resume(struct usb_interface *interface) ++{ ++ /*XXX: implement */ ++ ++ return 0; ++} ++#endif /* CONFIG_PM */ ++ ++static struct usb_device_id ps3_jupiter_devtab[] = { ++ { ++ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x054c, ++ .idProduct = 0x036f, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = 2, ++ .bInterfaceProtocol = 1 ++ }, ++ { } ++}; ++ ++static struct usb_driver ps3_jupiter_drv = { ++ .name = KBUILD_MODNAME, ++ .id_table = ps3_jupiter_devtab, ++ .probe = ps3_jupiter_probe, ++ .disconnect = ps3_jupiter_disconnect, ++#ifdef CONFIG_PM ++ .suspend = ps3_jupiter_suspend, ++ .resume = ps3_jupiter_resume, ++#endif /* CONFIG_PM */ ++}; ++ ++/* ++ * ps3_jupiter_init ++ */ ++static int __init ps3_jupiter_init(void) ++{ ++ return usb_register(&ps3_jupiter_drv); ++} ++ ++/* ++ * ps3_jupiter_exit ++ */ ++static void __exit ps3_jupiter_exit(void) ++{ ++ usb_deregister(&ps3_jupiter_drv); ++} ++ ++module_init(ps3_jupiter_init); ++module_exit(ps3_jupiter_exit); ++ ++MODULE_SUPPORTED_DEVICE("PS3 Jupiter"); ++MODULE_DEVICE_TABLE(usb, ps3_jupiter_devtab); ++MODULE_DESCRIPTION("PS3 Jupiter"); ++MODULE_AUTHOR("glevand"); ++MODULE_LICENSE("GPL"); +--- /dev/null 2012-11-01 00:09:58.397610712 -0800 ++++ b/drivers/net/wireless/ps3jupiter/ps3_jupiter_sta.c 2012-11-01 03:22:36.000000000 -0800 +@@ -0,0 +1,2934 @@ ++ ++/* ++ * PS3 Jupiter STA ++ * ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "ps3_eurus.h" ++#include "ps3_jupiter.h" ++ ++#define PS3_JUPITER_STA_CMD_BUFSIZE 2048 ++ ++#define PS3_JUPITER_STA_IFACE 0x4 ++#define PS3_JUPITER_STA_EP 0x6 ++ ++#define PS3_JUPITER_STA_WEP_KEYS 4 ++ ++#define PS3_JUPITER_STA_WPA_PSK_LENGTH 32 ++ ++#define PS3_JUPITER_STA_RX_URBS 4 ++#define PS3_JUPITER_STA_RX_BUFSIZE 0x620 ++ ++#define PS3_JUPITER_STA_TX_URBS 16 ++ ++#define PS3_JUPITER_STA_CHANNEL_DWELL 0x64 ++ ++#define PS3_JUPITER_STA_SCAN_VALID_TIME_SEC 60 ++ ++enum ps3_jupiter_sta_status { ++ PS3_JUPITER_STA_READY = 0, ++}; ++ ++enum ps3_jupiter_sta_scan_status { ++ PS3_JUPITER_STA_SCAN_INVALID = 0, ++ PS3_JUPITER_STA_SCAN_IN_PROGRESS, ++ PS3_JUPITER_STA_SCAN_OK ++}; ++ ++enum ps3_jupiter_sta_assoc_status { ++ PS3_JUPITER_STA_ASSOC_INVALID = 0, ++ PS3_JUPITER_STA_ASSOC_IN_PROGRESS, ++ PS3_JUPITER_STA_ASSOC_OK ++}; ++ ++enum ps3_jupiter_sta_config_bits { ++ PS3_JUPITER_STA_CONFIG_ESSID_SET = 0, ++ PS3_JUPITER_STA_CONFIG_BSSID_SET, ++ PS3_JUPITER_STA_CONFIG_CHANNEL_SET, ++ PS3_JUPITER_STA_CONFIG_WPA_PSK_SET ++}; ++ ++enum ps3_jupiter_sta_bss_type { ++ PS3_JUPITER_STA_BSS_TYPE_INFRA = 0, ++ PS3_JUPITER_STA_BSS_TYPE_ADHOC, ++}; ++ ++enum ps3_jupiter_sta_opmode { ++ PS3_JUPITER_STA_OPMODE_11B = 0, ++ PS3_JUPITER_STA_OPMODE_11G, ++ PS3_JUPITER_STA_OPMODE_11BG ++}; ++ ++enum ps3_jupiter_sta_auth_mode { ++ PS3_JUPITER_STA_AUTH_OPEN = 0, ++ PS3_JUPITER_STA_AUTH_SHARED_KEY ++}; ++ ++enum ps3_jupiter_sta_wpa_mode { ++ PS3_JUPITER_STA_WPA_MODE_NONE = 0, ++ PS3_JUPITER_STA_WPA_MODE_WPA, ++ PS3_JUPITER_STA_WPA_MODE_WPA2 ++}; ++ ++enum ps3_jupiter_sta_cipher_mode { ++ PS3_JUPITER_STA_CIPHER_NONE = 0, ++ PS3_JUPITER_STA_CIPHER_WEP, ++ PS3_JUPITER_STA_CIPHER_TKIP, ++ PS3_JUPITER_STA_CIPHER_AES ++}; ++ ++struct ps3_jupiter_sta_scan_result { ++ struct list_head list; ++ ++ u8 bssid[6]; ++ u16 capability; ++ u8 rssi; ++ ++ u8 *essid_ie; ++ u8 *ds_param_set_ie; ++ u8 *supp_rates_ie; ++ u8 *ext_supp_rates_ie; ++ u8 *rsn_ie; ++ u8 *wpa_ie; ++ ++ unsigned int ie_length; ++ u8 ie[0]; ++}; ++ ++struct ps3_jupiter_sta_dev { ++ struct net_device *netdev; ++ ++ struct usb_device *udev; ++ ++ spinlock_t lock; ++ ++ unsigned long status; ++ ++ struct iw_public_data wireless_data; ++ struct iw_statistics wireless_stat; ++ ++ struct notifier_block event_listener; ++ ++ u16 channel_info; ++ ++ struct mutex scan_lock; ++ struct list_head scan_result_list; ++ enum ps3_jupiter_sta_scan_status scan_status; ++ struct completion scan_done_comp; ++ unsigned long scan_expires; ++ ++ unsigned long config_status; ++ ++ enum ps3_jupiter_sta_bss_type bss_type; ++ ++ enum ps3_jupiter_sta_opmode opmode; ++ ++ enum ps3_jupiter_sta_auth_mode auth_mode; ++ ++ enum ps3_jupiter_sta_wpa_mode wpa_mode; ++ enum ps3_jupiter_sta_cipher_mode group_cipher_mode; ++ enum ps3_jupiter_sta_cipher_mode pairwise_cipher_mode; ++ ++ u8 essid[IW_ESSID_MAX_SIZE]; ++ unsigned int essid_length; ++ ++ u8 desired_bssid[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++ ++ u8 channel; ++ ++ unsigned long key_config_status; ++ u8 key[PS3_JUPITER_STA_WEP_KEYS][IW_ENCODING_TOKEN_MAX]; ++ unsigned int key_length[PS3_JUPITER_STA_WEP_KEYS]; ++ unsigned int curr_key_index; ++ ++ u8 psk[PS3_JUPITER_STA_WPA_PSK_LENGTH]; ++ ++ struct mutex assoc_lock; ++ struct workqueue_struct *assoc_queue; ++ struct delayed_work assoc_work; ++ enum ps3_jupiter_sta_assoc_status assoc_status; ++ struct completion assoc_done_comp; ++ ++ struct usb_anchor rx_urb_anchor; ++ struct sk_buff_head rx_skb_queue; ++ struct tasklet_struct rx_tasklet; ++ ++ struct usb_anchor tx_urb_anchor; ++ atomic_t tx_submitted_urbs; ++}; ++ ++static int max_txurbs = PS3_JUPITER_STA_TX_URBS; ++module_param(max_txurbs, int, S_IRUGO); ++MODULE_PARM_DESC(max_txurbs, "Maximum number of Tx URBs"); ++ ++static const int ps3_jupiter_sta_channel_freq[] = { ++ 2412, ++ 2417, ++ 2422, ++ 2427, ++ 2432, ++ 2437, ++ 2442, ++ 2447, ++ 2452, ++ 2457, ++ 2462, ++ 2467, ++ 2472, ++ 2484 ++}; ++ ++static const int ps3_jupiter_sta_bitrate[] = { ++ 1000000, ++ 2000000, ++ 5500000, ++ 11000000, ++ 6000000, ++ 9000000, ++ 12000000, ++ 18000000, ++ 24000000, ++ 36000000, ++ 48000000, ++ 54000000 ++}; ++ ++static void ps3_jupiter_sta_free_scan_results(struct ps3_jupiter_sta_dev *jstad); ++ ++static int ps3_jupiter_sta_start_scan(struct ps3_jupiter_sta_dev *jstad, ++ u8 *essid, size_t essid_length, u16 channels, u8 active, u16 channel_dwell); ++ ++static char *ps3_jupiter_sta_translate_scan_result(struct ps3_jupiter_sta_dev *jstad, ++ struct ps3_jupiter_sta_scan_result *scan_result, ++ struct iw_request_info *info, char *stream, char *ends); ++ ++static void ps3_jupiter_sta_start_assoc(struct ps3_jupiter_sta_dev *jstad); ++ ++static int ps3_jupiter_sta_disassoc(struct ps3_jupiter_sta_dev *jstad); ++ ++static void ps3_jupiter_sta_reset_state(struct ps3_jupiter_sta_dev *jstad); ++ ++static int ps3_jupiter_sta_prepare_rx_urb(struct ps3_jupiter_sta_dev *jstad, ++ struct urb *urb); ++ ++static void ps3_jupiter_sta_purge_rx_skb_queue(struct ps3_jupiter_sta_dev *jstad); ++ ++static void ps3_jupiter_sta_free_tx_urbs(struct ps3_jupiter_sta_dev *jstad); ++ ++static int ps3_jupiter_sta_tx_skb(struct ps3_jupiter_sta_dev *jstad, struct sk_buff *skb); ++ ++/* ++ * ps3_jupiter_sta_freq_to_channel ++ */ ++static u8 ps3_jupiter_sta_freq_to_channel(u32 freq) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq); i++) { ++ if (ps3_jupiter_sta_channel_freq[i] == freq) ++ return (i + 1); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_send_iw_ap_event ++ */ ++static void ps3_jupiter_sta_send_iw_ap_event(struct ps3_jupiter_sta_dev *jstad, u8 *bssid) ++{ ++ union iwreq_data iwrd; ++ ++ memset(&iwrd, 0, sizeof(iwrd)); ++ ++ if (bssid) ++ memcpy(iwrd.ap_addr.sa_data, bssid, ETH_ALEN); ++ ++ iwrd.ap_addr.sa_family = ARPHRD_ETHER; ++ ++ wireless_send_event(jstad->netdev, SIOCGIWAP, &iwrd, NULL); ++} ++ ++/* ++ * ps3_jupiter_sta_get_name ++ */ ++static int ps3_jupiter_sta_get_name(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ strcpy(wrqu->name, "IEEE 802.11bg"); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_nick ++ */ ++static int ps3_jupiter_sta_get_nick(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ strcpy(extra, "ps3_jupiter_sta"); ++ wrqu->data.length = strlen(extra); ++ wrqu->data.flags = 1; ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_range ++ */ ++static int ps3_jupiter_sta_get_range(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_point *point = &wrqu->data; ++ struct iw_range *range = (struct iw_range *) extra; ++ unsigned int i, chan; ++ ++ point->length = sizeof(struct iw_range); ++ memset(range, 0, sizeof(struct iw_range)); ++ ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 22; ++ ++ for (i = 0, chan = 0; ++ (i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq)) && (chan < IW_MAX_FREQUENCIES); i++) { ++ if (jstad->channel_info & (1 << i)) { ++ range->freq[chan].i = i + 1; ++ range->freq[chan].m = ps3_jupiter_sta_channel_freq[i]; ++ range->freq[chan].e = 6; ++ chan++; ++ } ++ } ++ ++ range->num_frequency = chan; ++ range->old_num_frequency = chan; ++ range->num_channels = chan; ++ range->old_num_channels = chan; ++ ++ for (i = 0; i < ARRAY_SIZE(ps3_jupiter_sta_bitrate); i++) ++ range->bitrate[i] = ps3_jupiter_sta_bitrate[i]; ++ range->num_bitrates = i; ++ ++ range->max_qual.qual = 100; ++ range->max_qual.level = 100; ++ range->avg_qual.qual = 50; ++ range->avg_qual.level = 50; ++ range->sensitivity = 0; ++ ++ IW_EVENT_CAPA_SET_KERNEL(range->event_capa); ++ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); ++ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); ++ ++ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | ++ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP | ++ IW_ENC_CAPA_4WAY_HANDSHAKE; ++ ++ range->encoding_size[0] = 5; ++ range->encoding_size[1] = 13; ++ range->encoding_size[2] = 32; ++ range->num_encoding_sizes = 3; ++ range->max_encoding_tokens = 4; ++ ++ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_CHANNEL; ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_mode ++ */ ++static int ps3_jupiter_sta_set_mode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ ++ switch (wrqu->mode) { ++ case IW_MODE_INFRA: ++ jstad->bss_type = PS3_JUPITER_STA_BSS_TYPE_INFRA; ++ break; ++ case IW_MODE_ADHOC: ++ jstad->bss_type = PS3_JUPITER_STA_BSS_TYPE_ADHOC; ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_mode ++ */ ++static int ps3_jupiter_sta_get_mode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ wrqu->mode = IW_MODE_INFRA; ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_freq ++ */ ++static int ps3_jupiter_sta_set_freq(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_freq *freq = &wrqu->freq; ++ __u8 channel; ++ unsigned long irq_flags; ++ int err; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (!freq->m) { ++ jstad->channel = 0; ++ clear_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status); ++ } else { ++ if (freq->e == 1) ++ channel = ps3_jupiter_sta_freq_to_channel(freq->m / 100000); ++ else ++ channel = freq->m; ++ ++ if (!channel || !(jstad->channel_info & (1 << (channel - 1)))) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ jstad->channel = channel; ++ set_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status); ++ } ++ ++ err = 0; ++ ++done: ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_freq ++ */ ++static int ps3_jupiter_sta_get_freq(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_freq *freq = &wrqu->freq; ++ unsigned long irq_flags; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (test_bit(PS3_JUPITER_STA_CONFIG_CHANNEL_SET, &jstad->config_status)) { ++ freq->e = 1; ++ freq->m = ps3_jupiter_sta_channel_freq[jstad->channel - 1] * 100000; ++ } else { ++ freq->e = 0; ++ freq->m = 0; ++ } ++ ++ pr_debug("%s: done\n", __func__); ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_scan ++ */ ++static int ps3_jupiter_sta_set_scan(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_scan_req *scan_req; ++ u8 *essid = NULL; ++ size_t essid_length = 0; ++ u16 channels = jstad->channel_info; ++ __u8 channel; ++ u8 active = 1; ++ u16 channel_dwell = PS3_JUPITER_STA_CHANNEL_DWELL; ++ int i; ++ int err; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ if (wrqu->data.length == sizeof(*scan_req)) { ++ scan_req = (struct iw_scan_req *) extra; ++ ++ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { ++ essid = scan_req->essid; ++ essid_length = scan_req->essid_len; ++ ++ pr_debug("%s: essid %s\n", __func__, essid); ++ } ++ ++ if (scan_req->num_channels > 0) ++ channels = 0; ++ ++ for (i = 0; i < scan_req->num_channels; i++) { ++ if (scan_req->channel_list[i].e == 1) ++ channel = ps3_jupiter_sta_freq_to_channel(scan_req->channel_list[i].m / 100000); ++ else ++ channel = scan_req->channel_list[i].m; ++ ++ channels |= 1 << (channel - 1); ++ } ++ ++ pr_debug("%s: channels 0x%04x\n", __func__, channels); ++ ++ active = (scan_req->scan_type == IW_SCAN_TYPE_ACTIVE); ++ } ++ ++ err = ps3_jupiter_sta_start_scan(jstad, essid, essid_length, channels, ++ active, channel_dwell); ++ ++ pr_debug("%s: done\n", __func__); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_scan ++ */ ++static int ps3_jupiter_sta_get_scan(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct ps3_jupiter_sta_scan_result *scan_result; ++ char *stream = extra; ++ char *ends = stream + wrqu->data.length; ++ int err; ++ ++ if (mutex_lock_interruptible(&jstad->scan_lock)) ++ return -EAGAIN; ++ ++ if (jstad->scan_status == PS3_JUPITER_STA_SCAN_IN_PROGRESS) { ++ err = -EAGAIN; ++ goto done; ++ } else if (jstad->scan_status == PS3_JUPITER_STA_SCAN_INVALID) { ++ err = -ENODEV; ++ goto done; ++ } ++ ++ /* translate scan results */ ++ ++ list_for_each_entry(scan_result, &jstad->scan_result_list, list) { ++ stream = ps3_jupiter_sta_translate_scan_result(jstad, scan_result, ++ info, stream, ends); ++ ++ if ((ends - stream) <= IW_EV_ADDR_LEN) { ++ err = -E2BIG; ++ goto done; ++ } ++ } ++ ++ wrqu->data.length = stream - extra; ++ wrqu->data.flags = 0; ++ ++ err = 0; ++ ++done: ++ ++ mutex_unlock(&jstad->scan_lock); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_set_auth ++ */ ++static int ps3_jupiter_sta_set_auth(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_param *param = &wrqu->param; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ switch (param->flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE; ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ } else if (param->value & IW_AUTH_WPA_VERSION_WPA) { ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA; ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP; ++ } else if (param->value & IW_AUTH_WPA_VERSION_WPA2) { ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA2; ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_AES; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_AES; ++ } ++ break; ++ case IW_AUTH_CIPHER_GROUP: ++ if (param->value & IW_AUTH_CIPHER_NONE) ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ else if (param->value & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ else if (param->value & IW_AUTH_CIPHER_TKIP) ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP; ++ else if (param->value & IW_AUTH_CIPHER_CCMP) ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_AES; ++ break; ++ case IW_AUTH_CIPHER_PAIRWISE: ++ if (param->value & IW_AUTH_CIPHER_NONE) ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ else if (param->value & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ else if (param->value & IW_AUTH_CIPHER_TKIP) ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_TKIP; ++ else if (param->value & IW_AUTH_CIPHER_CCMP) ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_AES; ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN; ++ else if (param->value & IW_AUTH_ALG_SHARED_KEY) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY; ++ else ++ err = -EINVAL; ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ if (param->value) ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_WPA; ++ else ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE; ++ break; ++ case IW_AUTH_KEY_MGMT: ++ if (!(param->value & IW_AUTH_KEY_MGMT_PSK)) ++ err = -EOPNOTSUPP; ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_auth ++ */ ++static int ps3_jupiter_sta_get_auth(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_param *param = &wrqu->param; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ switch (param->flags & IW_AUTH_INDEX) { ++ case IW_AUTH_WPA_VERSION: ++ switch (jstad->wpa_mode) { ++ case PS3_JUPITER_STA_WPA_MODE_WPA: ++ param->value |= IW_AUTH_WPA_VERSION_WPA; ++ break; ++ case PS3_JUPITER_STA_WPA_MODE_WPA2: ++ param->value |= IW_AUTH_WPA_VERSION_WPA2; ++ break; ++ default: ++ param->value |= IW_AUTH_WPA_VERSION_DISABLED; ++ } ++ break; ++ case IW_AUTH_80211_AUTH_ALG: ++ switch (jstad->auth_mode) { ++ case PS3_JUPITER_STA_AUTH_OPEN: ++ param->value |= IW_AUTH_ALG_OPEN_SYSTEM; ++ break; ++ case PS3_JUPITER_STA_AUTH_SHARED_KEY: ++ param->value |= IW_AUTH_ALG_SHARED_KEY; ++ break; ++ } ++ break; ++ case IW_AUTH_WPA_ENABLED: ++ switch (jstad->wpa_mode) { ++ case PS3_JUPITER_STA_WPA_MODE_WPA: ++ case PS3_JUPITER_STA_WPA_MODE_WPA2: ++ param->value = 1; ++ break; ++ default: ++ param->value = 0; ++ } ++ break; ++ default: ++ err = -EOPNOTSUPP; ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_set_essid ++ */ ++static int ps3_jupiter_sta_set_essid(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ unsigned long irq_flags; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ if (wrqu->essid.length > IW_ESSID_MAX_SIZE) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (wrqu->essid.flags) { ++ memcpy(jstad->essid, extra, wrqu->essid.length); ++ jstad->essid_length = wrqu->essid.length; ++ set_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status); ++ ++ pr_debug("%s: essid %s\n", __func__, extra); ++ } else { ++ clear_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status); ++ ++ pr_debug("%s: essid any\n", __func__); ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ ps3_jupiter_sta_start_assoc(jstad); ++ ++ pr_debug("%s: done\n", __func__); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_essid ++ */ ++static int ps3_jupiter_sta_get_essid(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status)) { ++ memcpy(extra, jstad->essid, jstad->essid_length); ++ wrqu->essid.length = jstad->essid_length; ++ wrqu->essid.flags = 1; ++ } else { ++ wrqu->essid.flags = 0; ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_ap ++ */ ++static int ps3_jupiter_sta_set_ap(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ unsigned long irq_flags; ++ ++ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (is_valid_ether_addr(wrqu->ap_addr.sa_data)) { ++ memcpy(jstad->desired_bssid, wrqu->ap_addr.sa_data, ETH_ALEN); ++ set_bit(PS3_JUPITER_STA_CONFIG_BSSID_SET, &jstad->config_status); ++ } else { ++ memset(jstad->desired_bssid, 0, ETH_ALEN); ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_ap ++ */ ++static int ps3_jupiter_sta_get_ap(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ wrqu->ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(wrqu->ap_addr.sa_data, jstad->desired_bssid, ETH_ALEN); ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_encode ++ */ ++static int ps3_jupiter_sta_set_encode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_point *enc = &wrqu->encoding; ++ __u16 flags = enc->flags & IW_ENCODE_FLAGS; ++ int key_index = enc->flags & IW_ENCODE_INDEX; ++ int key_index_provided; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ if (key_index > PS3_JUPITER_STA_WEP_KEYS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (key_index) { ++ key_index--; ++ key_index_provided = 1; ++ } else { ++ key_index = jstad->curr_key_index; ++ key_index_provided = 0; ++ } ++ ++ if (flags & IW_ENCODE_NOKEY) { ++ if (!(flags & ~IW_ENCODE_NOKEY) && key_index_provided) { ++ jstad->curr_key_index = key_index; ++ goto done; ++ } ++ ++ if (flags & IW_ENCODE_DISABLED) { ++ if (key_index_provided) { ++ clear_bit(key_index, &jstad->key_config_status); ++ } else { ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ jstad->key_config_status = 0; ++ } ++ } ++ ++ if (flags & IW_ENCODE_OPEN) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN; ++ else if (flags & IW_ENCODE_RESTRICTED) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY; ++ } else { ++ if (enc->length > IW_ENCODING_TOKEN_MAX) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ memcpy(jstad->key[key_index], extra, enc->length); ++ jstad->key_length[key_index] = enc->length; ++ set_bit(key_index, &jstad->key_config_status); ++ ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_WEP; ++ } ++ ++done: ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_encode ++ */ ++static int ps3_jupiter_sta_get_encode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_point *enc = &wrqu->encoding; ++ int key_index = enc->flags & IW_ENCODE_INDEX; ++ int key_index_provided; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ if (key_index > PS3_JUPITER_STA_WEP_KEYS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (key_index) { ++ key_index--; ++ key_index_provided = 1; ++ } else { ++ key_index = jstad->curr_key_index; ++ key_index_provided = 0; ++ } ++ ++ if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) { ++ switch (jstad->auth_mode) { ++ case PS3_JUPITER_STA_AUTH_OPEN: ++ enc->flags |= IW_ENCODE_OPEN; ++ break; ++ case PS3_JUPITER_STA_AUTH_SHARED_KEY: ++ enc->flags |= IW_ENCODE_RESTRICTED; ++ break; ++ } ++ } else { ++ enc->flags = IW_ENCODE_DISABLED; ++ } ++ ++ if (test_bit(key_index, &jstad->key_config_status)) { ++ if (enc->length < jstad->key_length[key_index]) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ memcpy(extra, jstad->key[key_index], jstad->key_length[key_index]); ++ enc->length = jstad->key_length[key_index]; ++ } else { ++ enc->length = 0; ++ enc->flags |= IW_ENCODE_NOKEY; ++ } ++ ++ enc->flags |= (key_index + 1); ++ ++done: ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_set_encodeext ++ */ ++static int ps3_jupiter_sta_set_encodeext(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_point *enc = &wrqu->encoding; ++ struct iw_encode_ext *enc_ext = (struct iw_encode_ext *) extra; ++ __u16 flags = enc->flags & IW_ENCODE_FLAGS; ++ int key_index = enc->flags & IW_ENCODE_INDEX; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ if (key_index > PS3_JUPITER_STA_WEP_KEYS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (key_index) ++ key_index--; ++ else ++ key_index = jstad->curr_key_index; ++ ++ if (!enc->length && (enc_ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { ++ jstad->curr_key_index = key_index; ++ } else if ((enc_ext->alg == IW_ENCODE_ALG_NONE) || (flags & IW_ENCODE_DISABLED)) { ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN; ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE; ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ } else if (enc_ext->alg == IW_ENCODE_ALG_WEP) { ++ if (flags & IW_ENCODE_OPEN) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN; ++ else if (flags & IW_ENCODE_RESTRICTED) ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_SHARED_KEY; ++ ++ if (enc_ext->key_len > IW_ENCODING_TOKEN_MAX) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ memcpy(jstad->key[key_index], enc_ext->key, enc_ext->key_len); ++ jstad->key_length[key_index] = enc_ext->key_len; ++ set_bit(key_index, &jstad->key_config_status); ++ } else if (enc_ext->alg == IW_ENCODE_ALG_PMK) { ++ if (enc_ext->key_len != PS3_JUPITER_STA_WPA_PSK_LENGTH) { ++ err = -EINVAL; ++ goto done; ++ } ++ ++ memcpy(jstad->psk, enc_ext->key, enc_ext->key_len); ++ set_bit(PS3_JUPITER_STA_CONFIG_WPA_PSK_SET, &jstad->config_status); ++ } ++ ++done: ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_encodeext ++ */ ++static int ps3_jupiter_sta_get_encodeext(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_point *enc = &wrqu->encoding; ++ struct iw_encode_ext *enc_ext = (struct iw_encode_ext *) extra; ++ int key_index = enc->flags & IW_ENCODE_INDEX; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ if ((enc->length - sizeof(struct iw_encode_ext)) < 0) ++ return -EINVAL; ++ ++ if (key_index > PS3_JUPITER_STA_WEP_KEYS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (key_index) ++ key_index--; ++ else ++ key_index = jstad->curr_key_index; ++ ++ memset(enc_ext, 0, sizeof(*enc_ext)); ++ ++ switch (jstad->group_cipher_mode) { ++ case PS3_JUPITER_STA_CIPHER_WEP: ++ enc_ext->alg = IW_ENCODE_ALG_WEP; ++ enc->flags |= IW_ENCODE_ENABLED; ++ break; ++ case PS3_JUPITER_STA_CIPHER_TKIP: ++ enc_ext->alg = IW_ENCODE_ALG_TKIP; ++ enc->flags |= IW_ENCODE_ENABLED; ++ break; ++ case PS3_JUPITER_STA_CIPHER_AES: ++ enc_ext->alg = IW_ENCODE_ALG_CCMP; ++ enc->flags |= IW_ENCODE_ENABLED; ++ break; ++ default: ++ enc_ext->alg = IW_ENCODE_ALG_NONE; ++ enc->flags |= IW_ENCODE_NOKEY; ++ } ++ ++ if (!(enc->flags & IW_ENCODE_NOKEY)) { ++ if ((enc->length - sizeof(struct iw_encode_ext)) < jstad->key_length[key_index]) { ++ err = -E2BIG; ++ goto done; ++ } ++ ++ if (test_bit(key_index, &jstad->key_config_status)) ++ memcpy(enc_ext->key, jstad->key[key_index], jstad->key_length[key_index]); ++ } ++ ++done: ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_set_genie ++ */ ++static int ps3_jupiter_sta_set_genie(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_get_genie ++ */ ++static int ps3_jupiter_sta_get_genie(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_set_mlme ++ */ ++static int ps3_jupiter_sta_set_mlme(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ struct iw_mlme *mlme = (struct iw_mlme *) extra; ++ ++ switch (mlme->cmd) { ++ case IW_MLME_DEAUTH: ++ break; ++ case IW_MLME_DISASSOC: ++ ps3_jupiter_sta_disassoc(jstad); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_WEXT_PRIV ++/* ++ * ps3_jupiter_sta_set_opmode ++ */ ++static int ps3_jupiter_sta_set_opmode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ int opmode = *(int *) extra; ++ unsigned long irq_flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ switch (opmode) { ++ case PS3_JUPITER_STA_OPMODE_11B: ++ case PS3_JUPITER_STA_OPMODE_11G: ++ case PS3_JUPITER_STA_OPMODE_11BG: ++ jstad->opmode = opmode; ++ break; ++ default: ++ err = -EINVAL; ++ } ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_opmode ++ */ ++static int ps3_jupiter_sta_get_opmode(struct net_device *netdev, ++ struct iw_request_info *info, union iwreq_data *wrqu, char *extra) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ memcpy(extra, &jstad->opmode, sizeof(jstad->opmode)); ++ wrqu->data.length = sizeof(jstad->opmode); ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ return 0; ++} ++#endif ++ ++/* ++ * ps3_jupiter_sta_get_wireless_stats ++ */ ++static struct iw_statistics *ps3_jupiter_sta_get_wireless_stats(struct net_device *netdev) ++{ ++ /* XXX: implement */ ++ ++ return NULL; ++} ++ ++/* ++ * ps3_jupiter_sta_open ++ */ ++static int ps3_jupiter_sta_open(struct net_device *netdev) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ ++ pr_debug("%s: called\n", __func__); ++ ++ usb_unpoison_anchored_urbs(&jstad->tx_urb_anchor); ++ ++ ps3_jupiter_sta_start_assoc(jstad); ++ ++ netif_start_queue(netdev); ++ ++ pr_debug("%s: done\n", __func__); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_stop ++ */ ++static int ps3_jupiter_sta_stop(struct net_device *netdev) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ ++ pr_debug("%s: called\n", __func__); ++ ++ netif_stop_queue(netdev); ++ ++ cancel_delayed_work(&jstad->assoc_work); ++ ++ if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK) ++ ps3_jupiter_sta_disassoc(jstad); ++ ++ tasklet_kill(&jstad->rx_tasklet); ++ ps3_jupiter_sta_purge_rx_skb_queue(jstad); ++ ++ ps3_jupiter_sta_free_tx_urbs(jstad); ++ ++ ps3_jupiter_sta_free_scan_results(jstad); ++ ++ ps3_jupiter_sta_reset_state(jstad); ++ ++ pr_debug("%s: done\n", __func__); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_start_xmit ++ */ ++static int ps3_jupiter_sta_start_xmit(struct sk_buff *skb, struct net_device *netdev) ++{ ++ struct ps3_jupiter_sta_dev *jstad = netdev_priv(netdev); ++ ++ return ps3_jupiter_sta_tx_skb(jstad, skb); ++} ++ ++/* ++ * ps3_jupiter_sta_set_rx_mode ++ */ ++static void ps3_jupiter_sta_set_rx_mode(struct net_device *netdev) ++{ ++ /* XXX: implement */ ++} ++ ++/* ++ * ps3_jupiter_sta_change_mtu ++ */ ++static int ps3_jupiter_sta_change_mtu(struct net_device *netdev, int new_mtu) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_tx_timeout ++ */ ++static void ps3_jupiter_sta_tx_timeout(struct net_device *netdev) ++{ ++ /* XXX: implement */ ++} ++ ++/* ++ * ps3_jupiter_sta_get_drvinfo ++ */ ++static void ps3_jupiter_sta_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) ++{ ++ /* XXX: implement */ ++} ++ ++/* ++ * ps3_jupiter_sta_get_link ++ */ ++static u32 ps3_jupiter_sta_get_link(struct net_device *netdev) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++ ++static const iw_handler ps3_jupiter_sta_iw_handler[] = ++{ ++ IW_HANDLER(SIOCGIWNAME, ps3_jupiter_sta_get_name), ++ IW_HANDLER(SIOCGIWNICKN, ps3_jupiter_sta_get_nick), ++ IW_HANDLER(SIOCGIWRANGE, ps3_jupiter_sta_get_range), ++ IW_HANDLER(SIOCSIWMODE, ps3_jupiter_sta_set_mode), ++ IW_HANDLER(SIOCGIWMODE, ps3_jupiter_sta_get_mode), ++ IW_HANDLER(SIOCSIWFREQ, ps3_jupiter_sta_set_freq), ++ IW_HANDLER(SIOCGIWFREQ, ps3_jupiter_sta_get_freq), ++ IW_HANDLER(SIOCSIWSCAN, ps3_jupiter_sta_set_scan), ++ IW_HANDLER(SIOCGIWSCAN, ps3_jupiter_sta_get_scan), ++ IW_HANDLER(SIOCSIWAUTH, ps3_jupiter_sta_set_auth), ++ IW_HANDLER(SIOCGIWAUTH, ps3_jupiter_sta_get_auth), ++ IW_HANDLER(SIOCSIWESSID, ps3_jupiter_sta_set_essid), ++ IW_HANDLER(SIOCGIWESSID, ps3_jupiter_sta_get_essid), ++ IW_HANDLER(SIOCSIWAP, ps3_jupiter_sta_set_ap), ++ IW_HANDLER(SIOCGIWAP, ps3_jupiter_sta_get_ap), ++ IW_HANDLER(SIOCSIWENCODE, ps3_jupiter_sta_set_encode), ++ IW_HANDLER(SIOCGIWENCODE, ps3_jupiter_sta_get_encode), ++ IW_HANDLER(SIOCSIWENCODEEXT, ps3_jupiter_sta_set_encodeext), ++ IW_HANDLER(SIOCGIWENCODEEXT, ps3_jupiter_sta_get_encodeext), ++ IW_HANDLER(SIOCSIWGENIE, ps3_jupiter_sta_set_genie), ++ IW_HANDLER(SIOCGIWGENIE, ps3_jupiter_sta_get_genie), ++ IW_HANDLER(SIOCSIWMLME, ps3_jupiter_sta_set_mlme), ++}; ++ ++#ifdef CONFIG_WEXT_PRIV ++static const iw_handler ps3_jupiter_sta_iw_priv_handler[] = { ++ ps3_jupiter_sta_set_opmode, ++ ps3_jupiter_sta_get_opmode, ++}; ++ ++enum { ++ PS3_JUPITER_STA_IW_PRIV_SET_OPMODE = SIOCIWFIRSTPRIV, ++ PS3_JUPITER_STA_IW_PRIV_GET_OPMODE, ++}; ++ ++static struct iw_priv_args ps3_jupiter_sta_iw_priv_args[] = { ++ { ++ .cmd = PS3_JUPITER_STA_IW_PRIV_SET_OPMODE, ++ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ .name = "set_opmode" ++ }, ++ { ++ .cmd = PS3_JUPITER_STA_IW_PRIV_GET_OPMODE, ++ .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ .name = "get_opmode" ++ }, ++}; ++#endif ++ ++static const struct iw_handler_def ps3_jupiter_sta_iw_handler_def = { ++ .standard = ps3_jupiter_sta_iw_handler, ++ .num_standard = ARRAY_SIZE(ps3_jupiter_sta_iw_handler), ++#ifdef CONFIG_WEXT_PRIV ++ .private = ps3_jupiter_sta_iw_priv_handler, ++ .num_private = ARRAY_SIZE(ps3_jupiter_sta_iw_priv_handler), ++ .private_args = ps3_jupiter_sta_iw_priv_args, ++ .num_private_args = ARRAY_SIZE(ps3_jupiter_sta_iw_priv_args), ++#endif ++ .get_wireless_stats = ps3_jupiter_sta_get_wireless_stats, ++}; ++ ++static const struct net_device_ops ps3_jupiter_sta_net_device_ops = { ++ .ndo_open = ps3_jupiter_sta_open, ++ .ndo_stop = ps3_jupiter_sta_stop, ++ .ndo_start_xmit = ps3_jupiter_sta_start_xmit, ++ .ndo_set_rx_mode = ps3_jupiter_sta_set_rx_mode, ++ .ndo_change_mtu = ps3_jupiter_sta_change_mtu, ++ .ndo_tx_timeout = ps3_jupiter_sta_tx_timeout, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static const struct ethtool_ops ps3_jupiter_sta_ethtool_ops = { ++ .get_drvinfo = ps3_jupiter_sta_get_drvinfo, ++ .get_link = ps3_jupiter_sta_get_link, ++}; ++ ++/* ++ * ps3_jupiter_sta_rx_urb_complete ++ */ ++static void ps3_jupiter_sta_rx_urb_complete(struct urb *urb) ++{ ++ struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(usb_ifnum_to_if(urb->dev, PS3_JUPITER_STA_IFACE)); ++ struct usb_device *udev = jstad->udev; ++ struct net_device *netdev = jstad->netdev; ++ struct sk_buff *skb = urb->context; ++ int err; ++ ++ dev_dbg(&udev->dev, "Rx URB completed (%d)\n", urb->status); ++ ++ switch (urb->status) { ++ case 0: ++ /* success */ ++ ++ if (urb->actual_length == 0x10) { ++ dev_info(&udev->dev, "got empty Rx URB\n"); ++ break; ++ } ++ ++ skb_put(skb, urb->actual_length); ++ ++ err = ps3_jupiter_sta_prepare_rx_urb(jstad, urb); ++ if (err) { ++ dev_err(&udev->dev, "could not prepare Rx URB (%d)\n", err); ++ break; ++ } ++ ++ skb_queue_tail(&jstad->rx_skb_queue, skb); ++ ++ tasklet_schedule(&jstad->rx_tasklet); ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ case -ENODEV: ++ goto free_skb; ++ break; ++ default: ++ netdev->stats.rx_errors++; ++ dev_err(&udev->dev, "Rx URB failed (%d)\n", urb->status); ++ } ++ ++ /* resubmit */ ++ ++ skb = urb->context; ++ skb_reset_tail_pointer(skb); ++ skb_trim(skb, 0); ++ ++ usb_anchor_urb(urb, &jstad->rx_urb_anchor); ++ ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err) { ++ netdev->stats.rx_errors++; ++ usb_unanchor_urb(urb); ++ dev_err(&udev->dev, "could not submit Rx URB (%d)\n", err); ++ goto free_skb; ++ } ++ ++ return; ++ ++free_skb: ++ ++ dev_kfree_skb_irq(skb); ++} ++ ++/* ++ * ps3_jupiter_sta_tx_urb_complete ++ */ ++static void ps3_jupiter_sta_tx_urb_complete(struct urb *urb) ++{ ++ struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(usb_ifnum_to_if(urb->dev, PS3_JUPITER_STA_IFACE)); ++ struct usb_device *udev = jstad->udev; ++ struct net_device *netdev = jstad->netdev; ++ struct sk_buff *skb = urb->context; ++ unsigned long irq_flags; ++ ++ dev_dbg(&udev->dev, "Tx URB completed (%d)\n", urb->status); ++ ++ switch (urb->status) { ++ case 0: ++ /* success */ ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ netdev->stats.tx_packets++; ++ netdev->stats.tx_bytes += skb->len; ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ atomic_dec(&jstad->tx_submitted_urbs); ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ case -ENODEV: ++ break; ++ default: ++ netdev->stats.tx_errors++; ++ dev_err(&udev->dev, "Tx URB failed (%d)\n", urb->status); ++ } ++ ++ dev_kfree_skb_irq(skb); ++} ++ ++/* ++ * ps3_jupiter_sta_tx_skb ++ */ ++static int ps3_jupiter_sta_tx_skb(struct ps3_jupiter_sta_dev *jstad, struct sk_buff *skb) ++{ ++ struct usb_device *udev = jstad->udev; ++ struct net_device *netdev = jstad->netdev; ++ struct urb *urb; ++ unsigned long irq_flags; ++ int err; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ if (jstad->assoc_status != PS3_JUPITER_STA_ASSOC_OK) { ++ err = 0; ++ goto drop; ++ } ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ ++ if (atomic_read(&jstad->tx_submitted_urbs) >= max_txurbs) { ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ dev_err(&udev->dev, "no free Tx URBs\n"); ++ err = -EBUSY; ++ goto drop; ++ } ++ ++ atomic_inc(&jstad->tx_submitted_urbs); ++ ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) { ++ atomic_dec(&jstad->tx_submitted_urbs); ++ dev_err(&udev->dev, "could not allocate Tx URB\n"); ++ err = -ENOMEM; ++ goto drop; ++ } ++ ++ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, PS3_JUPITER_STA_EP), ++ skb->data, skb->len, ps3_jupiter_sta_tx_urb_complete, skb); ++ urb->transfer_flags |= URB_ZERO_PACKET; ++ ++ usb_anchor_urb(urb, &jstad->tx_urb_anchor); ++ usb_free_urb(urb); ++ ++ err = usb_submit_urb(urb, GFP_ATOMIC); ++ if (err) { ++ atomic_dec(&jstad->tx_submitted_urbs); ++ usb_unanchor_urb(urb); ++ dev_err(&udev->dev, "could not submit Tx URB (%d)\n", err); ++ goto drop; ++ } ++ ++ pr_debug("%s: done\n", __func__); ++ ++ return 0; ++ ++drop: ++ ++ spin_lock_irqsave(&jstad->lock, irq_flags); ++ netdev->stats.tx_dropped++; ++ spin_unlock_irqrestore(&jstad->lock, irq_flags); ++ dev_kfree_skb_any(skb); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_free_scan_results ++ */ ++static void ps3_jupiter_sta_free_scan_results(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct ps3_jupiter_sta_scan_result *scan_result, *tmp; ++ ++ list_for_each_entry_safe(scan_result, tmp, &jstad->scan_result_list, list) { ++ list_del(&scan_result->list); ++ kfree(scan_result); ++ } ++} ++ ++/* ++ * ps3_jupiter_sta_start_scan ++ */ ++static int ps3_jupiter_sta_start_scan(struct ps3_jupiter_sta_dev *jstad, ++ u8 *essid, size_t essid_length, u16 channels, u8 active, u16 channel_dwell) ++{ ++ struct ps3_eurus_cmd_start_scan *eurus_cmd_start_scan; ++ struct usb_device *udev = jstad->udev; ++ unsigned char *buf = NULL; ++ unsigned int payload_length, status; ++ unsigned int i, chan; ++ u8 *chan_ie, *essid_ie; ++ int err; ++ ++ pr_debug("%s: called\n", __func__); ++ ++#ifdef DEBUG ++ if (essid && essid_length) ++ pr_debug("%s: essid %s\n", __func__, essid); ++#endif ++ ++ if (mutex_lock_interruptible(&jstad->scan_lock)) ++ return -ERESTARTSYS; ++ ++ if (jstad->scan_status == PS3_JUPITER_STA_SCAN_IN_PROGRESS) { ++ err = 0; ++ goto done; ++ } ++ ++ dev_dbg(&udev->dev, "starting new scan\n"); ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) { ++ err = -ENOMEM; ++ goto done; ++ } ++ ++ eurus_cmd_start_scan = (struct ps3_eurus_cmd_start_scan *) buf; ++ memset(eurus_cmd_start_scan, 0, 0x100); ++ eurus_cmd_start_scan->unknown2 = active; ++ eurus_cmd_start_scan->channel_dwell = cpu_to_le16(channel_dwell); ++ ++ chan_ie = eurus_cmd_start_scan->ie; ++ chan_ie[0] = WLAN_EID_DS_PARAMS; /* ie id */ ++ chan_ie[1] = 0x0; /* ie length */ ++ ++ for (i = 0, chan = 0; i < ARRAY_SIZE(ps3_jupiter_sta_channel_freq); i++) { ++ if (channels & (1 << i)) { ++ chan_ie[2 + chan] = i + 1; ++ chan++; ++ } ++ } ++ ++ chan_ie[1] = chan; /* ie length */ ++ payload_length = chan_ie + 2 + chan_ie[1] - (u8 *) eurus_cmd_start_scan; ++ ++ if (essid && essid_length) { ++ essid_ie = chan_ie + 2 + chan_ie[1]; ++ essid_ie[0] = WLAN_EID_SSID; /* ie id */ ++ essid_ie[1] = essid_length; /* ie length */ ++ memcpy(essid_ie + 2, essid, essid_length); ++ ++ payload_length += 2 + essid_ie[1]; ++ } ++ ++ init_completion(&jstad->scan_done_comp); ++ ++ jstad->scan_status = PS3_JUPITER_STA_SCAN_IN_PROGRESS; ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_START_SCAN, ++ eurus_cmd_start_scan, payload_length, &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ err = 0; ++ ++ pr_debug("%s: done\n", __func__); ++ ++done: ++ ++ if (err) ++ jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID; ++ ++ if (buf) ++ kfree(buf); ++ ++ mutex_unlock(&jstad->scan_lock); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_scan_results ++ */ ++static int ps3_jupiter_sta_get_scan_results(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct ps3_eurus_cmd_get_scan_results *eurus_cmd_get_scan_results; ++ struct ps3_eurus_scan_result *eurus_scan_result; ++ struct ps3_jupiter_sta_scan_result *scan_result; ++ unsigned char *buf; ++ unsigned int status, response_length; ++ size_t eurus_scan_result_length, ie_length; ++ unsigned int i; ++ u8 *ie; ++ int err; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ eurus_cmd_get_scan_results = (struct ps3_eurus_cmd_get_scan_results *) buf; ++ memset(eurus_cmd_get_scan_results, 0, PS3_EURUS_SCAN_RESULTS_MAXSIZE); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_SCAN_RESULTS, ++ eurus_cmd_get_scan_results, PS3_EURUS_SCAN_RESULTS_MAXSIZE, &status, ++ &response_length, eurus_cmd_get_scan_results); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* free old scan results */ ++ ++ ps3_jupiter_sta_free_scan_results(jstad); ++ ++ pr_debug("%s: number of scan results %d\n", __func__, eurus_cmd_get_scan_results->count); ++ ++ /* add each scan result to list */ ++ ++ for (i = 0, eurus_scan_result = eurus_cmd_get_scan_results->result; ++ i < eurus_cmd_get_scan_results->count; i++) { ++ eurus_scan_result_length = le16_to_cpu(eurus_scan_result->length) + ++ sizeof(eurus_scan_result->length); ++ ie_length = (u8 *) eurus_scan_result + eurus_scan_result_length - eurus_scan_result->ie; ++ ++ scan_result = kzalloc(sizeof(*scan_result) + ie_length, GFP_KERNEL); ++ if (!scan_result) ++ goto next; ++ ++ memcpy(scan_result->bssid, eurus_scan_result->bssid, sizeof(eurus_scan_result->bssid)); ++ scan_result->capability = le16_to_cpu(eurus_scan_result->capability); ++ scan_result->rssi = eurus_scan_result->rssi; ++ ++ memcpy(scan_result->ie, eurus_scan_result->ie, ie_length); ++ scan_result->ie_length = ie_length; ++ ++ for (ie = scan_result->ie; ++ ie < (scan_result->ie + scan_result->ie_length); ++ ie += (2 + ie[1])) { ++ switch (ie[0]) { ++ case WLAN_EID_SSID: ++ scan_result->essid_ie = ie; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ scan_result->supp_rates_ie = ie; ++ break; ++ case WLAN_EID_DS_PARAMS: ++ scan_result->ds_param_set_ie = ie; ++ break; ++ case WLAN_EID_RSN: ++ scan_result->rsn_ie = ie; ++ break; ++ case WLAN_EID_EXT_SUPP_RATES: ++ scan_result->ext_supp_rates_ie = ie; ++ break; ++ case WLAN_EID_VENDOR_SPECIFIC: ++ { ++ /* WPA */ ++ ++ static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2 }; ++ ++ if (((sizeof(wpa_oui) + 1) <= ie[1]) && ++ !memcmp(&ie[2], wpa_oui, sizeof(wpa_oui)) && ++ (ie[2 + sizeof(wpa_oui)] == 0x1)) ++ scan_result->wpa_ie = ie; ++ } ++ break; ++ } ++ } ++ ++ list_add_tail(&scan_result->list, &jstad->scan_result_list); ++ ++ next: ++ ++ /* move to next scan result */ ++ ++ eurus_scan_result = (struct ps3_eurus_scan_result *) ((u8 *) eurus_scan_result + ++ eurus_scan_result_length); ++ } ++ ++ err = 0; ++ ++ pr_debug("%s: done\n", __func__); ++ ++done: ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_translate_scan_result ++ */ ++static char *ps3_jupiter_sta_translate_scan_result(struct ps3_jupiter_sta_dev *jstad, ++ struct ps3_jupiter_sta_scan_result *scan_result, ++ struct iw_request_info *info, char *stream, char *ends) ++{ ++ struct iw_event iwe; ++ char *tmp; ++ unsigned int i; ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ memcpy(iwe.u.ap_addr.sa_data, scan_result->bssid, ETH_ALEN); ++ stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_ADDR_LEN); ++ ++ if (scan_result->essid_ie) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.flags = 1; ++ iwe.u.data.length = scan_result->essid_ie[1]; ++ stream = iwe_stream_add_point(info, stream, ends, &iwe, &scan_result->essid_ie[2]); ++ } ++ ++ if (scan_result->ds_param_set_ie) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = scan_result->ds_param_set_ie[2]; ++ iwe.u.freq.e = 0; ++ iwe.u.freq.i = 0; ++ stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_FREQ_LEN); ++ } ++ ++ tmp = stream + iwe_stream_lcp_len(info); ++ ++ if (scan_result->supp_rates_ie) { ++ for (i = 0; i < scan_result->supp_rates_ie[1]; i++) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWRATE; ++ iwe.u.bitrate.fixed = 0; ++ iwe.u.bitrate.disabled = 0; ++ iwe.u.bitrate.value = (scan_result->supp_rates_ie[2 + i] & 0x7f) * 500000; ++ tmp = iwe_stream_add_value(info, stream, tmp, ends, &iwe, IW_EV_PARAM_LEN); ++ } ++ } ++ ++ if (scan_result->ext_supp_rates_ie) { ++ for (i = 0; i < scan_result->ext_supp_rates_ie[1]; i++) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWRATE; ++ iwe.u.bitrate.fixed = 0; ++ iwe.u.bitrate.disabled = 0; ++ iwe.u.bitrate.value = (scan_result->ext_supp_rates_ie[2 + i] & 0x7f) * 500000; ++ tmp = iwe_stream_add_value(info, stream, tmp, ends, &iwe, IW_EV_PARAM_LEN); ++ } ++ } ++ ++ stream = tmp; ++ ++ iwe.cmd = SIOCGIWMODE; ++ if (scan_result->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { ++ if (scan_result->capability & WLAN_CAPABILITY_ESS) ++ iwe.u.mode = IW_MODE_MASTER; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = SIOCGIWENCODE; ++ if (scan_result->capability & WLAN_CAPABILITY_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->bssid); ++ ++ if (scan_result->rsn_ie) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = 2 + scan_result->rsn_ie[1]; ++ stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->rsn_ie); ++ } ++ ++ if (scan_result->wpa_ie) { ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVGENIE; ++ iwe.u.data.length = 2 + scan_result->wpa_ie[1]; ++ stream = iwe_stream_add_point(info, stream, ends, &iwe, scan_result->wpa_ie); ++ } ++ ++ memset(&iwe, 0, sizeof(iwe)); ++ iwe.cmd = IWEVQUAL; ++ iwe.u.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; ++ iwe.u.qual.level = ps3_eurus_rssi2percentage(scan_result->rssi); ++ iwe.u.qual.qual = ps3_eurus_rssi2percentage(scan_result->rssi); ++ iwe.u.qual.noise = 0; ++ stream = iwe_stream_add_event(info, stream, ends, &iwe, IW_EV_QUAL_LEN); ++ ++ return stream; ++} ++ ++/* ++ * ps3_jupiter_sta_find_best_scan_result ++ */ ++static struct ps3_jupiter_sta_scan_result *ps3_jupiter_sta_find_best_scan_result(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct ps3_jupiter_sta_scan_result *scan_result, *best_scan_result; ++ u8 *essid; ++ unsigned int essid_length; ++ ++ best_scan_result = NULL; ++ ++ /* traverse scan results */ ++ ++ list_for_each_entry(scan_result, &jstad->scan_result_list, list) { ++ if (scan_result->essid_ie) { ++ essid = &scan_result->essid_ie[2]; ++ essid_length = scan_result->essid_ie[1]; ++ } else { ++ essid = NULL; ++ essid_length = 0; ++ } ++ ++ if ((essid_length != jstad->essid_length) || ++ strncmp(essid, jstad->essid, essid_length)) ++ continue; ++ ++ if (test_bit(PS3_JUPITER_STA_CONFIG_BSSID_SET, &jstad->config_status)) { ++ if (!compare_ether_addr(jstad->desired_bssid, scan_result->bssid)) { ++ best_scan_result = scan_result; ++ break; ++ } else { ++ continue; ++ } ++ } ++ ++ switch (jstad->wpa_mode) { ++ case PS3_JUPITER_STA_WPA_MODE_NONE: ++ if ((jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) && ++ !(scan_result->capability & WLAN_CAPABILITY_PRIVACY)) ++ continue; ++ break; ++ case PS3_JUPITER_STA_WPA_MODE_WPA: ++ if (!scan_result->wpa_ie) ++ continue; ++ break; ++ case PS3_JUPITER_STA_WPA_MODE_WPA2: ++ if (!scan_result->rsn_ie) ++ continue; ++ break; ++ } ++ ++ if (!best_scan_result || (best_scan_result->rssi > scan_result->rssi)) ++ best_scan_result = scan_result; ++ } ++ ++ return best_scan_result; ++} ++ ++/* ++ * ps3_jupiter_sta_assoc ++ */ ++static int ps3_jupiter_sta_assoc(struct ps3_jupiter_sta_dev *jstad, ++ struct ps3_jupiter_sta_scan_result *scan_result) ++{ ++ struct ps3_eurus_cmd_0x1ed *eurus_cmd_0x1ed; ++ struct ps3_eurus_cmd_0x1025 *eurus_cmd_0x1025; ++ struct ps3_eurus_cmd_common_config *eurus_cmd_common_config; ++ struct ps3_eurus_cmd_wep_config *eurus_cmd_wep_config; ++ struct ps3_eurus_cmd_wpa_config *eurus_cmd_wpa_config; ++ struct ps3_eurus_cmd_associate *eurus_cmd_associate; ++ unsigned char *buf = NULL; ++ unsigned int payload_length, status; ++ u8 *ie; ++ int err; ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ eurus_cmd_0x1ed = (struct ps3_eurus_cmd_0x1ed *) buf; ++ memset(eurus_cmd_0x1ed, 0, sizeof(*eurus_cmd_0x1ed)); ++ eurus_cmd_0x1ed->unknown2 = 0x1; ++ eurus_cmd_0x1ed->unknown3 = 0x2; ++ eurus_cmd_0x1ed->unknown4 = 0xff; ++ eurus_cmd_0x1ed->unknown5 = 0x16; /*XXX: 0x4 if AP doesn't support rate 54Mbps */ ++ eurus_cmd_0x1ed->unknown6 = 0x4; ++ eurus_cmd_0x1ed->unknown7 = 0xa; ++ eurus_cmd_0x1ed->unknown8 = 0x16; /*XXX: 0x4 if AP doesn't support rate 54Mbps */ ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x1ed, ++ eurus_cmd_0x1ed, sizeof(*eurus_cmd_0x1ed), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* set preamble mode */ ++ ++ eurus_cmd_0x1025 = (struct ps3_eurus_cmd_0x1025 *) buf; ++ memset(eurus_cmd_0x1025, 0, sizeof(*eurus_cmd_0x1025)); ++ ++ if (scan_result->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) ++ eurus_cmd_0x1025->preamble_mode = PS3_EURUS_PREAMBLE_SHORT; ++ else ++ eurus_cmd_0x1025->preamble_mode = PS3_EURUS_PREAMBLE_LONG; ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x1025, ++ eurus_cmd_0x1025, sizeof(*eurus_cmd_0x1025), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* set common configuration */ ++ ++ eurus_cmd_common_config = (struct ps3_eurus_cmd_common_config *) buf; ++ memset(eurus_cmd_common_config, 0, sizeof(*eurus_cmd_common_config)); ++ ++ switch (jstad->bss_type) { ++ case PS3_JUPITER_STA_BSS_TYPE_INFRA: ++ eurus_cmd_common_config->bss_type = PS3_EURUS_BSS_INFRA; ++ break; ++ case PS3_JUPITER_STA_BSS_TYPE_ADHOC: ++ eurus_cmd_common_config->bss_type = PS3_EURUS_BSS_ADHOC; ++ break; ++ } ++ ++ switch (jstad->auth_mode) { ++ case PS3_JUPITER_STA_AUTH_OPEN: ++ eurus_cmd_common_config->auth_mode = PS3_EURUS_AUTH_OPEN; ++ break; ++ case PS3_JUPITER_STA_AUTH_SHARED_KEY: ++ eurus_cmd_common_config->auth_mode = PS3_EURUS_AUTH_SHARED_KEY; ++ break; ++ } ++ ++ switch (jstad->opmode) { ++ case PS3_JUPITER_STA_OPMODE_11B: ++ eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11B; ++ break; ++ case PS3_JUPITER_STA_OPMODE_11G: ++ eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11G; ++ break; ++ case PS3_JUPITER_STA_OPMODE_11BG: ++ eurus_cmd_common_config->opmode = PS3_EURUS_OPMODE_11BG; ++ break; ++ } ++ ++ memcpy(eurus_cmd_common_config->bssid, scan_result->bssid, sizeof(scan_result->bssid)); ++ ++ eurus_cmd_common_config->capability = cpu_to_le16(scan_result->capability & ~WLAN_CAPABILITY_QOS); ++ ++ payload_length = sizeof(*eurus_cmd_common_config); ++ ++ ie = eurus_cmd_common_config->ie; ++ ++ ie[0] = WLAN_EID_SSID; ++ ie[1] = jstad->essid_length; ++ memcpy(&ie[2], jstad->essid, jstad->essid_length); ++ ++ payload_length += (2 + ie[1]); ++ ie += (2 + ie[1]); ++ ++ if (scan_result->ds_param_set_ie) { ++ memcpy(ie, scan_result->ds_param_set_ie, 2 + scan_result->ds_param_set_ie[1]); ++ ++ payload_length += (2 + ie[1]); ++ ie += (2 + ie[1]); ++ } ++ ++ if (scan_result->supp_rates_ie) { ++ memcpy(ie, scan_result->supp_rates_ie, 2 + scan_result->supp_rates_ie[1]); ++ ++ payload_length += (2 + scan_result->supp_rates_ie[1]); ++ ie += (2 + scan_result->supp_rates_ie[1]); ++ } ++ ++ if (scan_result->ext_supp_rates_ie) { ++ memcpy(ie, scan_result->ext_supp_rates_ie, 2 + scan_result->ext_supp_rates_ie[1]); ++ ++ payload_length += (2 + scan_result->ext_supp_rates_ie[1]); ++ ie += (2 + scan_result->ext_supp_rates_ie[1]); ++ } ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_COMMON_CONFIG, ++ eurus_cmd_common_config, payload_length, &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ if (jstad->wpa_mode == PS3_JUPITER_STA_WPA_MODE_NONE) { ++ /* set WEP configuration */ ++ ++ /* XXX: implement */ ++ ++ eurus_cmd_wep_config = (struct ps3_eurus_cmd_wep_config *) buf; ++ memset(eurus_cmd_wep_config, 0, sizeof(*eurus_cmd_wep_config)); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_WEP_CONFIG, ++ eurus_cmd_wep_config, sizeof(*eurus_cmd_wep_config), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ } else { ++ /* set WPA configuration */ ++ ++ eurus_cmd_wpa_config = (struct ps3_eurus_cmd_wpa_config *) buf; ++ memset(eurus_cmd_wpa_config, 0, sizeof(*eurus_cmd_wpa_config)); ++ ++ eurus_cmd_wpa_config->unknown = 0x1; ++ ++ switch (jstad->wpa_mode) { ++ case PS3_JUPITER_STA_WPA_MODE_WPA: ++ eurus_cmd_wpa_config->security_mode = PS3_EURUS_WPA_SECURITY_WPA; ++ if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP) ++ eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP; ++ else ++ eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES; ++ if (jstad->pairwise_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP) ++ eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_TKIP; ++ else ++ eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA_AES; ++ eurus_cmd_wpa_config->akm_suite = PS3_EURUS_WPA_AKM_SUITE_WPA_PSK; ++ break; ++ case PS3_JUPITER_STA_WPA_MODE_WPA2: ++ eurus_cmd_wpa_config->security_mode = PS3_EURUS_WPA_SECURITY_WPA2; ++ if (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP) ++ eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP; ++ else ++ eurus_cmd_wpa_config->group_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES; ++ if (jstad->pairwise_cipher_mode == PS3_JUPITER_STA_CIPHER_TKIP) ++ eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_TKIP; ++ else ++ eurus_cmd_wpa_config->pairwise_cipher_suite = PS3_EURUS_WPA_CIPHER_SUITE_WPA2_AES; ++ eurus_cmd_wpa_config->akm_suite = PS3_EURUS_WPA_AKM_SUITE_WPA2_PSK; ++ break; ++ default: ++ /* should never happen */ ++ BUG(); ++ } ++ ++ eurus_cmd_wpa_config->psk_type = PS3_EURUS_WPA_PSK_BIN; ++ memcpy(eurus_cmd_wpa_config->psk, jstad->psk, sizeof(jstad->psk)); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_WPA_CONFIG, ++ eurus_cmd_wpa_config, sizeof(*eurus_cmd_wpa_config), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ } ++ ++ init_completion(&jstad->assoc_done_comp); ++ ++ jstad->assoc_status = PS3_JUPITER_STA_ASSOC_IN_PROGRESS; ++ ++ eurus_cmd_associate = (struct ps3_eurus_cmd_associate *) buf; ++ memset(eurus_cmd_associate, 0, sizeof(*eurus_cmd_associate)); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_ASSOCIATE, ++ eurus_cmd_associate, sizeof(*eurus_cmd_associate), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ err = wait_for_completion_timeout(&jstad->assoc_done_comp, 5 * HZ); ++ if (!err) { ++ /* timeout */ ++ ps3_jupiter_sta_disassoc(jstad); ++ err = -EIO; ++ goto done; ++ } ++ ++ jstad->assoc_status = PS3_JUPITER_STA_ASSOC_OK; ++ ++ memcpy(jstad->bssid, scan_result->bssid, sizeof(scan_result->bssid)); ++ ++ err = 0; ++ ++done: ++ ++ if (err) ++ jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID; ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_assoc_worker ++ */ ++static void ps3_jupiter_sta_assoc_worker(struct work_struct *work) ++{ ++ struct ps3_jupiter_sta_dev *jstad = container_of(work, struct ps3_jupiter_sta_dev, assoc_work.work); ++ struct usb_device *udev = jstad->udev; ++ u8 *essid; ++ unsigned int essid_length; ++ int scan_lock = 0; ++ struct ps3_jupiter_sta_scan_result *best_scan_result; ++ int err; ++ ++ mutex_lock(&jstad->assoc_lock); ++ ++ if (jstad->assoc_status != PS3_JUPITER_STA_ASSOC_INVALID) { ++ mutex_unlock(&jstad->assoc_lock); ++ return; ++ } ++ ++ dev_dbg(&udev->dev, "starting new association\n"); ++ ++ if ((jstad->scan_status != PS3_JUPITER_STA_SCAN_OK) || ++ time_after_eq(jiffies, jstad->scan_expires)) { ++ /* start scan and wait for scan results */ ++ ++ if (test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status)) { ++ essid = jstad->essid; ++ essid_length = jstad->essid_length; ++ } else { ++ essid = NULL; ++ essid_length = 0; ++ } ++ ++ err = ps3_jupiter_sta_start_scan(jstad, essid, essid_length, jstad->channel_info, ++ 1, PS3_JUPITER_STA_CHANNEL_DWELL); ++ if (err) ++ goto done; ++ ++ wait_for_completion(&jstad->scan_done_comp); ++ } ++ ++ mutex_lock(&jstad->scan_lock); ++ scan_lock = 1; ++ ++ if (jstad->scan_status != PS3_JUPITER_STA_SCAN_OK) ++ goto done; ++ ++ best_scan_result = ps3_jupiter_sta_find_best_scan_result(jstad); ++ if (!best_scan_result) { ++ dev_dbg(&udev->dev, "no suitable scan result was found\n"); ++ goto done; ++ } ++ ++ err = ps3_jupiter_sta_assoc(jstad, best_scan_result); ++ if (err) { ++ dev_dbg(&udev->dev, "association failed (%d)\n", err); ++ goto done; ++ } ++ ++done: ++ ++ if (scan_lock) ++ mutex_unlock(&jstad->scan_lock); ++ ++ if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK) ++ ps3_jupiter_sta_send_iw_ap_event(jstad, jstad->bssid); ++ else ++ ps3_jupiter_sta_send_iw_ap_event(jstad, NULL); ++ ++ mutex_unlock(&jstad->assoc_lock); ++} ++ ++/* ++ * ps3_jupiter_sta_start_assoc ++ */ ++static void ps3_jupiter_sta_start_assoc(struct ps3_jupiter_sta_dev *jstad) ++{ ++ pr_debug("%s: called\n", __func__); ++ ++ if (!test_bit(PS3_JUPITER_STA_READY, &jstad->status)) ++ return; ++ ++ if (!test_bit(PS3_JUPITER_STA_CONFIG_ESSID_SET, &jstad->config_status)) ++ return; ++ ++ if ((jstad->wpa_mode == PS3_JUPITER_STA_WPA_MODE_NONE) && ++ (jstad->group_cipher_mode == PS3_JUPITER_STA_CIPHER_WEP) && ++ !jstad->key_config_status) ++ return; ++ ++ if ((jstad->wpa_mode != PS3_JUPITER_STA_WPA_MODE_NONE) && ++ !test_bit(PS3_JUPITER_STA_CONFIG_WPA_PSK_SET, &jstad->config_status)) ++ return; ++ ++ queue_delayed_work(jstad->assoc_queue, &jstad->assoc_work, 0); ++ ++ pr_debug("%s: done\n", __func__); ++} ++ ++/* ++ * ps3_jupiter_sta_disassoc ++ */ ++static int ps3_jupiter_sta_disassoc(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct ps3_eurus_cmd_disassociate *eurus_cmd_disassociate; ++ unsigned char *buf = NULL; ++ unsigned int status; ++ int err; ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ eurus_cmd_disassociate = (struct ps3_eurus_cmd_disassociate *) buf; ++ memset(eurus_cmd_disassociate, 0, sizeof(*eurus_cmd_disassociate)); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_DISASSOCIATE, ++ eurus_cmd_disassociate, sizeof(*eurus_cmd_disassociate), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ err = 0; ++ ++done: ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_event_scan_completed ++ */ ++static void ps3_jupiter_sta_event_scan_completed(struct ps3_jupiter_sta_dev *jstad) ++{ ++ union iwreq_data iwrd; ++ int err; ++ ++ mutex_lock(&jstad->scan_lock); ++ ++ err = ps3_jupiter_sta_get_scan_results(jstad); ++ if (err) ++ goto done; ++ ++ jstad->scan_expires = jiffies + PS3_JUPITER_STA_SCAN_VALID_TIME_SEC * HZ; ++ jstad->scan_status = PS3_JUPITER_STA_SCAN_OK; ++ ++ complete(&jstad->scan_done_comp); ++ ++ memset(&iwrd, 0, sizeof(iwrd)); ++ wireless_send_event(jstad->netdev, SIOCGIWSCAN, &iwrd, NULL); ++ ++done: ++ ++ if (err) ++ jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID; ++ ++ mutex_unlock(&jstad->scan_lock); ++} ++ ++/* ++ * ps3_jupiter_sta_event_connected ++ */ ++static void ps3_jupiter_sta_event_connected(struct ps3_jupiter_sta_dev *jstad, u32 event_id) ++{ ++ u32 expected_event_id = 0; ++ ++ switch (jstad->wpa_mode) { ++ case PS3_JUPITER_STA_WPA_MODE_NONE: ++ expected_event_id = PS3_EURUS_EVENT_CONNECTED; ++ break; ++ case PS3_JUPITER_STA_WPA_MODE_WPA: ++ case PS3_JUPITER_STA_WPA_MODE_WPA2: ++ expected_event_id = PS3_EURUS_EVENT_WPA_CONNECTED; ++ break; ++ default: ++ return; ++ } ++ ++ if (expected_event_id == event_id) { ++ complete(&jstad->assoc_done_comp); ++ netif_carrier_on(jstad->netdev); ++ } ++} ++ ++/* ++ * ps3_jupiter_sta_event_disconnected ++ */ ++static void ps3_jupiter_sta_event_disconnected(struct ps3_jupiter_sta_dev *jstad) ++{ ++ int assoc_lock = 0; ++ ++ if (mutex_trylock(&jstad->assoc_lock)) ++ assoc_lock = 1; ++ ++ ps3_jupiter_sta_disassoc(jstad); ++ ++ if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK) ++ ps3_jupiter_sta_send_iw_ap_event(jstad, NULL); ++ ++ jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID; ++ ++ netif_carrier_off(jstad->netdev); ++ ++ if (assoc_lock) ++ mutex_unlock(&jstad->assoc_lock); ++} ++ ++/* ++ * ps3_jupiter_sta_event_handler ++ */ ++static int ps3_jupiter_sta_event_handler(struct notifier_block *n, ++ unsigned long val, void *v) ++{ ++ struct ps3_jupiter_sta_dev *jstad = container_of(n, struct ps3_jupiter_sta_dev, event_listener); ++ struct usb_device *udev = jstad->udev; ++ struct ps3_eurus_event *event = v; ++ ++ dev_dbg(&udev->dev, "got event (0x%08x 0x%08x 0x%08x 0x%08x 0x%08x)\n", ++ event->hdr.type, event->hdr.id, event->hdr.timestamp, ++ event->hdr.payload_length, event->hdr.unknown); ++ ++ switch (event->hdr.type) { ++ case PS3_EURUS_EVENT_TYPE_0x40: ++ switch (event->hdr.id) { ++ case PS3_EURUS_EVENT_DEAUTH: ++ ps3_jupiter_sta_event_disconnected(jstad); ++ break; ++ } ++ break; ++ case PS3_EURUS_EVENT_TYPE_0x80: ++ switch (event->hdr.id) { ++ case PS3_EURUS_EVENT_SCAN_COMPLETED: ++ ps3_jupiter_sta_event_scan_completed(jstad); ++ break; ++ case PS3_EURUS_EVENT_CONNECTED: ++ case PS3_EURUS_EVENT_WPA_CONNECTED: ++ ps3_jupiter_sta_event_connected(jstad, event->hdr.id); ++ break; ++ case PS3_EURUS_EVENT_BEACON_LOST: ++ ps3_jupiter_sta_event_disconnected(jstad); ++ break; ++ } ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++/* ++ * ps3_jupiter_sta_set_mac_addr ++ */ ++static int ps3_jupiter_sta_set_mac_addr(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct usb_device *udev = jstad->udev; ++ struct net_device *netdev = jstad->netdev; ++ struct ps3_eurus_cmd_get_mac_addr_list *eurus_cmd_get_mac_addr_list; ++ struct ps3_eurus_cmd_set_mac_addr *eurus_cmd_set_mac_addr; ++ struct ps3_eurus_cmd_0x115b *eurus_cmd_0x115b; ++ unsigned char *buf = NULL; ++ unsigned int status, response_length; ++ int err; ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ /* get MAC address list */ ++ ++ eurus_cmd_get_mac_addr_list = (struct ps3_eurus_cmd_get_mac_addr_list *) buf; ++ memset(eurus_cmd_get_mac_addr_list, 0, PS3_EURUS_MAC_ADDR_LIST_MAXSIZE); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_MAC_ADDR_LIST, ++ eurus_cmd_get_mac_addr_list, PS3_EURUS_MAC_ADDR_LIST_MAXSIZE, &status, ++ &response_length, eurus_cmd_get_mac_addr_list); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ /* use first MAC address */ ++ ++ memcpy(netdev->dev_addr, eurus_cmd_get_mac_addr_list->mac_addr, ETH_ALEN); ++ ++ dev_info(&udev->dev, "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", ++ netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], ++ netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); ++ ++ /* set MAC address */ ++ ++ eurus_cmd_set_mac_addr = (struct ps3_eurus_cmd_set_mac_addr *) buf; ++ memset(eurus_cmd_set_mac_addr, 0, sizeof(*eurus_cmd_set_mac_addr)); ++ memcpy(eurus_cmd_set_mac_addr->mac_addr, netdev->dev_addr, ETH_ALEN); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_SET_MAC_ADDR, ++ eurus_cmd_set_mac_addr, sizeof(*eurus_cmd_set_mac_addr), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ eurus_cmd_0x115b = (struct ps3_eurus_cmd_0x115b *) buf; ++ memset(eurus_cmd_0x115b, 0, sizeof(*eurus_cmd_0x115b)); ++ eurus_cmd_0x115b->unknown1 = cpu_to_le16(0x1); ++ eurus_cmd_0x115b->unknown2 = cpu_to_le16(0x0); ++ memcpy(eurus_cmd_0x115b->mac_addr, netdev->dev_addr, ETH_ALEN); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_0x115b, ++ eurus_cmd_0x115b, sizeof(*eurus_cmd_0x115b), &status, NULL, NULL); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ err = 0; ++ ++done: ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_get_channel_info ++ */ ++static int ps3_jupiter_sta_get_channel_info(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct ps3_eurus_cmd_get_channel_info *eurus_cmd_get_channel_info; ++ unsigned char *buf = NULL; ++ unsigned int status, response_length; ++ int err; ++ ++ buf = kmalloc(PS3_JUPITER_STA_CMD_BUFSIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ eurus_cmd_get_channel_info = (struct ps3_eurus_cmd_get_channel_info *) buf; ++ memset(eurus_cmd_get_channel_info, 0, sizeof(*eurus_cmd_get_channel_info)); ++ ++ err = ps3_jupiter_exec_eurus_cmd(PS3_EURUS_CMD_GET_CHANNEL_INFO, ++ eurus_cmd_get_channel_info, sizeof(*eurus_cmd_get_channel_info), &status, ++ &response_length, eurus_cmd_get_channel_info); ++ if (err) ++ goto done; ++ ++ if (status != PS3_EURUS_CMD_OK) { ++ err = -EIO; ++ goto done; ++ } ++ ++ jstad->channel_info = eurus_cmd_get_channel_info->channel_info; ++ ++ err = 0; ++ ++done: ++ ++ kfree(buf); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_reset_state ++ */ ++static void ps3_jupiter_sta_reset_state(struct ps3_jupiter_sta_dev *jstad) ++{ ++ jstad->scan_status = PS3_JUPITER_STA_SCAN_INVALID; ++ ++ jstad->config_status = 0; ++ ++ jstad->opmode = PS3_JUPITER_STA_OPMODE_11G; ++ ++ jstad->auth_mode = PS3_JUPITER_STA_AUTH_OPEN; ++ ++ jstad->wpa_mode = PS3_JUPITER_STA_WPA_MODE_NONE; ++ jstad->group_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ jstad->pairwise_cipher_mode = PS3_JUPITER_STA_CIPHER_NONE; ++ ++ memset(jstad->essid, 0, sizeof(jstad->essid)); ++ jstad->essid_length = 0; ++ ++ memset(jstad->desired_bssid, 0, sizeof(jstad->desired_bssid)); ++ ++ jstad->channel = 0; ++ ++ jstad->key_config_status = 0; ++ jstad->curr_key_index = 0; ++ ++ jstad->assoc_status = PS3_JUPITER_STA_ASSOC_INVALID; ++} ++ ++/* ++ * ps3_jupiter_sta_create_assoc_worker ++ */ ++static int ps3_jupiter_sta_create_assoc_worker(struct ps3_jupiter_sta_dev *jstad) ++{ ++ jstad->assoc_queue = create_singlethread_workqueue("ps3_jupiter_sta_assoc"); ++ if (!jstad->assoc_queue) ++ return -ENOMEM; ++ ++ INIT_DELAYED_WORK(&jstad->assoc_work, ps3_jupiter_sta_assoc_worker); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_destroy_assoc_worker ++ */ ++static void ps3_jupiter_sta_destroy_assoc_worker(struct ps3_jupiter_sta_dev *jstad) ++{ ++ if (jstad->assoc_queue) { ++ cancel_delayed_work(&jstad->assoc_work); ++ flush_workqueue(jstad->assoc_queue); ++ destroy_workqueue(jstad->assoc_queue); ++ jstad->assoc_queue = NULL; ++ } ++} ++ ++/* ++ * ps3_jupiter_sta_prepare_rx_urb ++ */ ++static int ps3_jupiter_sta_prepare_rx_urb(struct ps3_jupiter_sta_dev *jstad, ++ struct urb *urb) ++{ ++ struct usb_device *udev = jstad->udev; ++ struct sk_buff *skb; ++ ++ skb = dev_alloc_skb(PS3_JUPITER_STA_RX_BUFSIZE); ++ if (!skb) ++ return -ENOMEM; ++ ++ usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, PS3_JUPITER_STA_EP), ++ skb->data, PS3_JUPITER_STA_RX_BUFSIZE, ps3_jupiter_sta_rx_urb_complete, skb); ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_alloc_rx_urbs ++ */ ++static int ps3_jupiter_sta_alloc_rx_urbs(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct usb_device *udev = jstad->udev; ++ struct urb *urb; ++ unsigned int i; ++ int err; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ init_usb_anchor(&jstad->rx_urb_anchor); ++ ++ for (i = 0; i < PS3_JUPITER_STA_RX_URBS; i++) { ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ dev_err(&udev->dev, "could not allocate Rx URB\n"); ++ err = -ENOMEM; ++ goto done; ++ } ++ ++ err = ps3_jupiter_sta_prepare_rx_urb(jstad, urb); ++ if (err) { ++ dev_err(&udev->dev, "could not prepare Rx URB (%d)\n", err); ++ usb_free_urb(urb); ++ goto done; ++ } ++ ++ usb_anchor_urb(urb, &jstad->rx_urb_anchor); ++ usb_free_urb(urb); ++ ++ err = usb_submit_urb(urb, GFP_KERNEL); ++ if (err) { ++ dev_err(&udev->dev, "could not submit Rx URB (%d)\n", err); ++ dev_kfree_skb_any(urb->context); ++ usb_unanchor_urb(urb); ++ goto done; ++ } ++ } ++ ++ err = 0; ++ ++ pr_debug("%s: done\n", __func__); ++ ++done: ++ ++ if (err) ++ usb_kill_anchored_urbs(&jstad->rx_urb_anchor); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_free_rx_urbs ++ */ ++static void ps3_jupiter_sta_free_rx_urbs(struct ps3_jupiter_sta_dev *jstad) ++{ ++ usb_kill_anchored_urbs(&jstad->rx_urb_anchor); ++ ++ usb_poison_anchored_urbs(&jstad->rx_urb_anchor); ++} ++ ++/* ++ * ps3_jupiter_sta_rx_tasklet ++ */ ++static void ps3_jupiter_sta_rx_tasklet(unsigned long data) ++{ ++ struct ps3_jupiter_sta_dev *jstad = (struct ps3_jupiter_sta_dev *) data; ++ struct net_device *netdev = jstad->netdev; ++ struct sk_buff *skb; ++ ++ while ((skb = skb_dequeue(&jstad->rx_skb_queue))) { ++ skb->protocol = eth_type_trans(skb, netdev); ++ ++ netdev->stats.rx_packets++; ++ netdev->stats.rx_bytes += skb->len; ++ ++ netif_receive_skb(skb); ++ } ++} ++ ++/* ++ * ps3_jupiter_sta_purge_rx_skb_queue ++ */ ++static void ps3_jupiter_sta_purge_rx_skb_queue(struct ps3_jupiter_sta_dev *jstad) ++{ ++ struct sk_buff *skb; ++ unsigned long irq_flags; ++ ++ spin_lock_irqsave(&jstad->rx_skb_queue.lock, irq_flags); ++ ++ while ((skb = __skb_dequeue(&jstad->rx_skb_queue))) ++ dev_kfree_skb_any(skb); ++ ++ spin_unlock_irqrestore(&jstad->rx_skb_queue.lock, irq_flags); ++} ++ ++/* ++ * ps3_jupiter_sta_free_tx_urbs ++ */ ++static void ps3_jupiter_sta_free_tx_urbs(struct ps3_jupiter_sta_dev *jstad) ++{ ++ usb_wait_anchor_empty_timeout(&jstad->tx_urb_anchor, msecs_to_jiffies(100)); ++ ++ usb_kill_anchored_urbs(&jstad->tx_urb_anchor); ++ ++ usb_poison_anchored_urbs(&jstad->tx_urb_anchor); ++} ++ ++/* ++ * ps3_jupiter_sta_probe ++ */ ++static int ps3_jupiter_sta_probe(struct usb_interface *interface, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(interface); ++ struct ps3_jupiter_sta_dev *jstad; ++ struct net_device *netdev; ++ int err; ++ ++ netdev = alloc_etherdev(sizeof(struct ps3_jupiter_sta_dev)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ SET_NETDEV_DEV(netdev, &udev->dev); ++ ++ strcpy(netdev->name, "wlan%d"); ++ ++ netdev->ethtool_ops = &ps3_jupiter_sta_ethtool_ops; ++ netdev->netdev_ops = &ps3_jupiter_sta_net_device_ops; ++ netdev->wireless_data = &jstad->wireless_data; ++ netdev->wireless_handlers = &ps3_jupiter_sta_iw_handler_def; ++ ++ jstad = netdev_priv(netdev); ++ jstad->netdev = netdev; ++ ++ jstad->udev = usb_get_dev(udev); ++ usb_set_intfdata(interface, jstad); ++ ++ err = ps3_jupiter_sta_set_mac_addr(jstad); ++ if (err) { ++ dev_err(&udev->dev, "could not setup network device (%d)\n", err); ++ goto fail_free_netdev; ++ } ++ ++ spin_lock_init(&jstad->lock); ++ ++ jstad->event_listener.notifier_call = ps3_jupiter_sta_event_handler; ++ ++ err = ps3_jupiter_register_event_listener(&jstad->event_listener); ++ if (err) { ++ dev_err(&udev->dev, "could not register event listener (%d)\n", err); ++ goto fail_free_netdev; ++ } ++ ++ mutex_init(&jstad->scan_lock); ++ INIT_LIST_HEAD(&jstad->scan_result_list); ++ ++ err = ps3_jupiter_sta_get_channel_info(jstad); ++ if (err) { ++ dev_err(&udev->dev, "could not get channel info (%d)\n", err); ++ goto fail_unregister_event_listener; ++ } ++ ++ mutex_init(&jstad->assoc_lock); ++ ++ err = ps3_jupiter_sta_create_assoc_worker(jstad); ++ if (err) { ++ dev_err(&udev->dev, "could not create assoc work queue (%d)\n", err); ++ goto fail_unregister_event_listener; ++ } ++ ++ skb_queue_head_init(&jstad->rx_skb_queue); ++ tasklet_init(&jstad->rx_tasklet, ps3_jupiter_sta_rx_tasklet, (unsigned long) jstad); ++ ++ err = ps3_jupiter_sta_alloc_rx_urbs(jstad); ++ if (err) { ++ dev_err(&udev->dev, "could not allocate Rx URBs (%d)\n", err); ++ goto fail_destroy_assoc_worker; ++ } ++ ++ init_usb_anchor(&jstad->tx_urb_anchor); ++ atomic_set(&jstad->tx_submitted_urbs, 0); ++ ++ ps3_jupiter_sta_reset_state(jstad); ++ ++ set_bit(PS3_JUPITER_STA_READY, &jstad->status); ++ ++ err = register_netdev(netdev); ++ if (err) { ++ dev_dbg(&udev->dev, "could not register network device %s (%d)\n", netdev->name, err); ++ goto fail_free_rx_urbs; ++ } ++ ++ return 0; ++ ++fail_free_rx_urbs: ++ ++ ps3_jupiter_sta_free_rx_urbs(jstad); ++ ++fail_destroy_assoc_worker: ++ ++ ps3_jupiter_sta_destroy_assoc_worker(jstad); ++ ++fail_unregister_event_listener: ++ ++ ps3_jupiter_unregister_event_listener(&jstad->event_listener); ++ ++fail_free_netdev: ++ ++ usb_set_intfdata(interface, NULL); ++ usb_put_dev(udev); ++ ++ free_netdev(netdev); ++ ++ return err; ++} ++ ++/* ++ * ps3_jupiter_sta_disconnect ++ */ ++static void ps3_jupiter_sta_disconnect(struct usb_interface *interface) ++{ ++ struct ps3_jupiter_sta_dev *jstad = usb_get_intfdata(interface); ++ struct usb_device *udev = jstad->udev; ++ struct net_device *netdev = jstad->netdev; ++ ++ clear_bit(PS3_JUPITER_STA_READY, &jstad->status); ++ ++ unregister_netdev(netdev); ++ ++ if (jstad->assoc_status == PS3_JUPITER_STA_ASSOC_OK) ++ ps3_jupiter_sta_disassoc(jstad); ++ ++ ps3_jupiter_sta_destroy_assoc_worker(jstad); ++ ++ ps3_jupiter_sta_free_rx_urbs(jstad); ++ tasklet_kill(&jstad->rx_tasklet); ++ ps3_jupiter_sta_purge_rx_skb_queue(jstad); ++ ++ ps3_jupiter_sta_free_tx_urbs(jstad); ++ ++ ps3_jupiter_sta_free_scan_results(jstad); ++ ++ ps3_jupiter_unregister_event_listener(&jstad->event_listener); ++ ++ usb_set_intfdata(interface, NULL); ++ usb_put_dev(udev); ++ ++ free_netdev(netdev); ++} ++ ++#ifdef CONFIG_PM ++/* ++ * ps3_jupiter_sta_suspend ++ */ ++static int ps3_jupiter_sta_suspend(struct usb_interface *interface, pm_message_t state) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++ ++/* ++ * ps3_jupiter_sta_resume ++ */ ++static int ps3_jupiter_sta_resume(struct usb_interface *interface) ++{ ++ /* XXX: implement */ ++ ++ return 0; ++} ++#endif /* CONFIG_PM */ ++ ++static struct usb_device_id ps3_jupiter_sta_devtab[] = { ++ { ++ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x054c, ++ .idProduct = 0x036f, ++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, ++ .bInterfaceSubClass = 2, ++ .bInterfaceProtocol = 2 ++ }, ++ { } ++}; ++ ++static struct usb_driver ps3_jupiter_sta_drv = { ++ .name = KBUILD_MODNAME, ++ .id_table = ps3_jupiter_sta_devtab, ++ .probe = ps3_jupiter_sta_probe, ++ .disconnect = ps3_jupiter_sta_disconnect, ++#ifdef CONFIG_PM ++ .suspend = ps3_jupiter_sta_suspend, ++ .resume = ps3_jupiter_sta_resume, ++#endif /* CONFIG_PM */ ++}; ++ ++/* ++ * ps3_jupiter_sta_init ++ */ ++static int __init ps3_jupiter_sta_init(void) ++{ ++ return usb_register(&ps3_jupiter_sta_drv); ++} ++ ++/* ++ * ps3_jupiter_sta_exit ++ */ ++static void __exit ps3_jupiter_sta_exit(void) ++{ ++ usb_deregister(&ps3_jupiter_sta_drv); ++} ++ ++module_init(ps3_jupiter_sta_init); ++module_exit(ps3_jupiter_sta_exit); ++ ++MODULE_SUPPORTED_DEVICE("PS3 Jupiter STA"); ++MODULE_DEVICE_TABLE(usb, ps3_jupiter_sta_devtab); ++MODULE_DESCRIPTION("PS3 Jupiter STA"); ++MODULE_AUTHOR("glevand"); ++MODULE_LICENSE("GPL"); diff --git a/0160-gelic-disable-eurus-ctrl-iface.patch b/0160-gelic-disable-eurus-ctrl-iface.patch new file mode 100644 index 0000000..13fd6bf --- /dev/null +++ b/0160-gelic-disable-eurus-ctrl-iface.patch @@ -0,0 +1,78 @@ +--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c 2012-01-26 01:39:32.000000000 +0100 ++++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c 2012-01-31 20:54:24.798382231 +0100 +@@ -1671,12 +1671,75 @@ + { + struct gelic_card *card; + struct net_device *netdev; ++ u64 lpar_id, laid, hwconfig, eurus_lpar_access, junk; + int result; + + pr_debug("%s: called\n", __func__); + + udbg_shutdown_ps3gelic(); + ++ /* ++ * Check for eurus control interface and disable it when it is enabled. ++ * NB: it must be done before opening the gelic device else it will not work. ++ */ ++ ++ result = lv1_get_logical_partition_id(&lpar_id); ++ if (result) ++ goto open_hv_device; ++ ++ result = lv1_read_repository_node(1, ++ 0x0000000073730000ul /* ss */, ++ 0x6c61696400000000ul /* laid */, ++ lpar_id, ++ 0, ++ &laid, &junk); ++ if (result || (laid != 0x1070000002000001ul)) ++ goto open_hv_device; ++ ++ result = lv1_read_repository_node(1, ++ 0x0000000073797300ul /* sys */, ++ 0x6877000000000000ul /* hw */, ++ 0x636f6e6669670000ul /* config */, ++ 0, ++ &hwconfig, &junk); ++ if (result || !(hwconfig & 0x40000ul)) ++ goto open_hv_device; ++ ++ result = lv1_read_repository_node(1, ++ 0x00000000696f7300ul /* ios */, ++ 0x6e65740000000000ul /* net */, ++ 0x6575727573000000ul /* eurus */, ++ 0x6c70617200000000ul /* lpar */, ++ &eurus_lpar_access, &junk); ++ if (result) ++ goto open_hv_device; ++ ++ if (!(eurus_lpar_access & (1ul << lpar_id))) { ++ dev_info(&dev->core, "%s: eurus control interface is already disabled\n", ++ __func__); ++ goto open_hv_device; ++ } else { ++ dev_info(&dev->core, "%s: eurus control interface is enabled\n", ++ __func__); ++ } ++ ++ eurus_lpar_access &= ~(1ul << lpar_id); ++ ++ result = lv1_write_repository_node(1, ++ 0x00000000696f7300ul /* ios */, ++ 0x6e65740000000000ul /* net */, ++ 0x6575727573000000ul /* eurus */, ++ 0x6c70617200000000ul /* lpar */, ++ eurus_lpar_access, junk); ++ if (result) ++ dev_info(&dev->core, "%s: eurus control interface could not be disabled\n", ++ __func__); ++ else ++ dev_info(&dev->core, "%s: eurus control interface was disabled\n", ++ __func__); ++ ++open_hv_device: ++ + result = ps3_open_hv_device(dev); + + if (result) { diff --git a/0170-gelic-wireless-print-cmd-status.patch b/0170-gelic-wireless-print-cmd-status.patch new file mode 100644 index 0000000..0bddb83 --- /dev/null +++ b/0170-gelic-wireless-print-cmd-status.patch @@ -0,0 +1,11 @@ +--- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c 2012-01-26 01:39:32.000000000 +0100 ++++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c 2012-02-03 15:42:14.996356826 +0100 +@@ -183,7 +183,7 @@ + &cmd->tag, &cmd->size); + if (cmd->status) { + complete(&cmd->done); +- pr_info("%s: cmd issue failed\n", __func__); ++ pr_info("%s: cmd (%d) issue failed (%d)\n", __func__, cmd->cmd, cmd->status); + return; + } + diff --git a/0180-lv1call-add-undocumented-spe-hvcalls.patch b/0180-lv1call-add-undocumented-spe-hvcalls.patch new file mode 100644 index 0000000..2cf35d0 --- /dev/null +++ b/0180-lv1call-add-undocumented-spe-hvcalls.patch @@ -0,0 +1,72 @@ +--- a/arch/powerpc/include/asm/lv1call.h 2012-07-19 22:27:03.699665219 +0200 ++++ b/arch/powerpc/include/asm/lv1call.h 2012-07-19 22:27:32.146333542 +0200 +@@ -53,6 +53,7 @@ + #define LV1_5_IN_0_OUT_ARG_DECL LV1_5_IN_ARG_DECL + #define LV1_6_IN_0_OUT_ARG_DECL LV1_6_IN_ARG_DECL + #define LV1_7_IN_0_OUT_ARG_DECL LV1_7_IN_ARG_DECL ++#define LV1_8_IN_0_OUT_ARG_DECL LV1_8_IN_ARG_DECL + + #define LV1_0_IN_1_OUT_ARG_DECL LV1_1_OUT_ARG_DECL + #define LV1_1_IN_1_OUT_ARG_DECL LV1_1_IN_ARG_DECL, LV1_1_OUT_ARG_DECL +@@ -143,6 +144,7 @@ + #define LV1_5_IN_0_OUT_ARGS LV1_5_IN_ARGS + #define LV1_6_IN_0_OUT_ARGS LV1_6_IN_ARGS + #define LV1_7_IN_0_OUT_ARGS LV1_7_IN_ARGS ++#define LV1_8_IN_0_OUT_ARGS LV1_8_IN_ARGS + + #define LV1_0_IN_1_OUT_ARGS LV1_1_OUT_ARGS + #define LV1_1_IN_1_OUT_ARGS LV1_1_IN_ARGS, LV1_1_OUT_ARGS +@@ -253,6 +255,7 @@ + LV1_CALL(destruct_logical_spe, 1, 0, 54 ) + LV1_CALL(construct_logical_spe, 7, 6, 57 ) + LV1_CALL(set_spe_interrupt_mask, 3, 0, 61 ) ++LV1_CALL(undocumented_function_62, 5, 0, 62 ) + LV1_CALL(set_spe_transition_notifier, 3, 0, 64 ) + LV1_CALL(disable_logical_spe, 2, 0, 65 ) + LV1_CALL(clear_spe_interrupt_status, 4, 0, 66 ) +@@ -269,6 +272,7 @@ + LV1_CALL(remove_repository_node, 5, 0, 93 ) + LV1_CALL(read_htab_entries, 2, 5, 95 ) + LV1_CALL(set_dabr, 2, 0, 96 ) ++LV1_CALL(undocumented_function_99, 2, 0, 99 ) + LV1_CALL(get_total_execution_time, 2, 1, 103 ) + LV1_CALL(undocumented_function_114, 3, 1, 114 ) + LV1_CALL(undocumented_function_115, 1, 0, 115 ) +@@ -279,12 +283,15 @@ + LV1_CALL(map_htab, 1, 1, 122 ) + LV1_CALL(unmap_htab, 1, 0, 123 ) + LV1_CALL(get_version_info, 0, 2, 127 ) ++LV1_CALL(undocumented_function_138, 2, 0, 138 ) + LV1_CALL(insert_htab_entry, 6, 3, 158 ) + LV1_CALL(read_virtual_uart, 3, 1, 162 ) + LV1_CALL(write_virtual_uart, 3, 1, 163 ) + LV1_CALL(set_virtual_uart_param, 3, 0, 164 ) + LV1_CALL(get_virtual_uart_param, 2, 1, 165 ) + LV1_CALL(configure_virtual_uart_irq, 1, 1, 166 ) ++LV1_CALL(undocumented_function_167, 2, 1, 167 ) ++LV1_CALL(undocumented_function_168, 3, 0, 168 ) + LV1_CALL(open_device, 3, 0, 170 ) + LV1_CALL(close_device, 2, 0, 171 ) + LV1_CALL(map_device_mmio_region, 5, 1, 172 ) +@@ -305,8 +312,11 @@ + LV1_CALL(connect_interrupt_event_receive_port, 4, 0, 197 ) + LV1_CALL(disconnect_interrupt_event_receive_port, 4, 0, 198 ) + LV1_CALL(get_spe_all_interrupt_statuses, 1, 1, 199 ) ++LV1_CALL(undocumented_function_200, 1, 0, 200 ) ++LV1_CALL(undocumented_function_201, 1, 0, 201 ) + LV1_CALL(deconfigure_virtual_uart_irq, 0, 0, 202 ) + LV1_CALL(enable_logical_spe, 2, 0, 207 ) ++LV1_CALL(undocumented_function_209, 8, 0, 209 ) + LV1_CALL(gpu_open, 1, 0, 210 ) + LV1_CALL(gpu_close, 0, 0, 211 ) + LV1_CALL(gpu_device_map, 1, 2, 212 ) +--- a/arch/powerpc/platforms/ps3/hvcall.S 2012-07-19 22:27:10.499665615 +0200 ++++ b/arch/powerpc/platforms/ps3/hvcall.S 2012-07-19 22:27:12.892999087 +0200 +@@ -45,6 +45,7 @@ + #define LV1_5_IN_0_OUT LV1_N_IN_0_OUT + #define LV1_6_IN_0_OUT LV1_N_IN_0_OUT + #define LV1_7_IN_0_OUT LV1_N_IN_0_OUT ++#define LV1_8_IN_0_OUT LV1_N_IN_0_OUT + + #define LV1_0_IN_1_OUT(API_NAME, API_NUMBER) \ + _GLOBAL(_##API_NAME) \ diff --git a/0190-export-spe-irq-setup-destroy.patch b/0190-export-spe-irq-setup-destroy.patch new file mode 100644 index 0000000..2b66825 --- /dev/null +++ b/0190-export-spe-irq-setup-destroy.patch @@ -0,0 +1,18 @@ +--- a/arch/powerpc/platforms/ps3/interrupt.c 2012-06-22 20:37:50.000000000 +0200 ++++ b/arch/powerpc/platforms/ps3/interrupt.c 2012-07-15 11:54:14.122392086 +0200 +@@ -608,6 +608,7 @@ + + return result; + } ++EXPORT_SYMBOL_GPL(ps3_spe_irq_setup); + + int ps3_spe_irq_destroy(unsigned int virq) + { +@@ -620,6 +621,7 @@ + + return result; + } ++EXPORT_SYMBOL_GPL(ps3_spe_irq_destroy); + + + #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) diff --git a/0200-export-event-receive-port-destroy.patch b/0200-export-event-receive-port-destroy.patch new file mode 100644 index 0000000..e1073d1 --- /dev/null +++ b/0200-export-event-receive-port-destroy.patch @@ -0,0 +1,10 @@ +--- a/arch/powerpc/platforms/ps3/interrupt.c 2012-07-20 19:47:50.309201716 +0200 ++++ b/arch/powerpc/platforms/ps3/interrupt.c 2012-07-20 19:48:29.185870645 +0200 +@@ -381,6 +381,7 @@ + DBG(" <- %s:%d\n", __func__, __LINE__); + return result; + } ++EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy); + + int ps3_send_event_locally(unsigned int virq) + { diff --git a/0210-ps3encdec.patch b/0210-ps3encdec.patch new file mode 100644 index 0000000..6944c46 --- /dev/null +++ b/0210-ps3encdec.patch @@ -0,0 +1,499 @@ +--- a/arch/powerpc/include/asm/ps3.h 2012-08-02 23:17:17.126972935 +0200 ++++ b/arch/powerpc/include/asm/ps3.h 2012-08-06 19:41:56.754901977 +0200 +@@ -328,6 +328,7 @@ + PS3_MATCH_ID_LPM = 11, + PS3_MATCH_ID_STOR_NOR_FLASH = 12, + PS3_MATCH_ID_DISP_MANAGER = 13, ++ PS3_MATCH_ID_STOR_ENCDEC = 14, + }; + + enum ps3_match_sub_id { +@@ -349,6 +350,7 @@ + #define PS3_MODULE_ALIAS_LPM "ps3:11:0" + #define PS3_MODULE_ALIAS_STOR_NOR_FLASH "ps3:12:0" + #define PS3_MODULE_ALIAS_DISP_MANAGER "ps3:13:0" ++#define PS3_MODULE_ALIAS_STOR_ENCDEC "ps3:14:0" + + enum ps3_system_bus_device_type { + PS3_DEVICE_TYPE_IOC0 = 1, +--- a/arch/powerpc/platforms/ps3/platform.h 2012-08-02 23:17:17.110306267 +0200 ++++ b/arch/powerpc/platforms/ps3/platform.h 2012-08-06 19:42:30.948237298 +0200 +@@ -89,6 +89,7 @@ + PS3_DEV_TYPE_SB_GPIO = 6, + PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ + PS3_DEV_TYPE_STOR_NOR_FLASH = 254, ++ PS3_DEV_TYPE_STOR_ENCDEC = 255, + }; + + int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, +--- a/arch/powerpc/platforms/ps3/device-init.c 2012-08-02 23:17:17.130306269 +0200 ++++ b/arch/powerpc/platforms/ps3/device-init.c 2012-08-06 19:43:12.538239719 +0200 +@@ -621,6 +621,13 @@ + __func__, __LINE__); + break; + ++ case PS3_DEV_TYPE_STOR_ENCDEC: ++ result = ps3_setup_storage_dev(repo, PS3_MATCH_ID_STOR_ENCDEC); ++ if (result) ++ pr_debug("%s:%u ps3_setup_storage_dev failed\n", ++ __func__, __LINE__); ++ break; ++ + default: + result = 0; + pr_debug("%s:%u: unsupported dev_type %u\n", __func__, __LINE__, +--- a/arch/powerpc/platforms/ps3/system-bus.c 2012-08-02 23:17:17.126972935 +0200 ++++ b/arch/powerpc/platforms/ps3/system-bus.c 2012-08-06 19:44:03.851576042 +0200 +@@ -175,6 +175,7 @@ + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + case PS3_MATCH_ID_STOR_NOR_FLASH: ++ case PS3_MATCH_ID_STOR_ENCDEC: + return ps3_open_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: +@@ -215,6 +216,7 @@ + case PS3_MATCH_ID_STOR_ROM: + case PS3_MATCH_ID_STOR_FLASH: + case PS3_MATCH_ID_STOR_NOR_FLASH: ++ case PS3_MATCH_ID_STOR_ENCDEC: + return ps3_close_hv_device_sb(dev); + + case PS3_MATCH_ID_SOUND: +--- a/drivers/ps3/ps3stor_lib.c 2012-08-02 23:17:17.100306267 +0200 ++++ b/drivers/ps3/ps3stor_lib.c 2012-08-06 20:43:55.981785017 +0200 +@@ -90,8 +90,9 @@ + unsigned int i; + unsigned long n; + +- if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) { +- /* special case: CD-ROM is assumed always accessible */ ++ if ((dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) || ++ (dev->sbd.match_id == PS3_MATCH_ID_STOR_ENCDEC)) { ++ /* special case: CD-ROM and ENCDEC are assumed always accessible */ + dev->accessible_regions = 1; + return 0; + } +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-08-02 23:17:17.153639603 +0200 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-08-06 19:46:13.304916903 +0200 +@@ -192,6 +192,16 @@ + This driver allows you to create/delete/modify regions + on PS3 storage devices. + ++config PS3_ENCDEC ++ tristate "PS3 ENCDEC Driver" ++ depends on PPC_PS3 ++ select PS3_STORAGE ++ help ++ Include support for the PS3 ENCDEC device. ++ ++ This support is required to access the PS3 ENCDEC device. ++ In general, all users will say Y or M. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- a/drivers/char/Makefile 2012-08-02 23:17:17.153639603 +0200 ++++ b/drivers/char/Makefile 2012-08-06 19:47:18.001587333 +0200 +@@ -67,3 +67,4 @@ + + obj-$(CONFIG_PS3_PHYSMEM) += ps3physmem.o + obj-$(CONFIG_PS3_STRGMNGR) += ps3strgmngr.o ++obj-$(CONFIG_PS3_ENCDEC) += ps3encdec.o +--- /dev/null 2012-08-07 02:54:53.492474007 +0200 ++++ b/drivers/char/ps3encdec.c 2012-08-07 02:56:38.822480157 +0200 +@@ -0,0 +1,394 @@ ++/* ++ * PS3 ENCDEC Driver ++ * ++ * Copyright (C) 2011 graf_chokolo ++ * Copyright (C) 2011, 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define DEVICE_NAME "ps3encdec" ++ ++#define BOUNCE_SIZE (4 * 1024) ++ ++struct ps3encdec_private ++{ ++ struct ps3_storage_device *dev; ++ struct miscdevice misc; ++ char *bounce_wbuf; ++ u64 bounce_wlpar; ++ char *bounce_rbuf; ++ u64 bounce_rlpar; ++ struct mutex mtx; ++ wait_queue_head_t read_wq; ++ wait_queue_head_t write_wq; ++ int cmd_done; ++ int cmd_failed; ++ int cmd_data_avail; ++}; ++ ++static struct ps3encdec_private *ps3encdec_priv; ++ ++static ssize_t ps3encdec_read(struct file *file, char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ struct ps3encdec_private *priv = ps3encdec_priv; ++ int res = 0; ++ ++ if (mutex_lock_interruptible(&priv->mtx)) ++ return (-ERESTARTSYS); ++ ++ if (file->f_flags & O_NONBLOCK) { ++ if (!priv->cmd_done || priv->cmd_failed) ++ res = -EAGAIN; ++ } else { ++ DEFINE_WAIT(__wait); ++ ++ while (1) { ++ prepare_to_wait(&priv->read_wq, &__wait, TASK_INTERRUPTIBLE); ++ ++ if (priv->cmd_data_avail) ++ break; ++ ++ mutex_unlock(&priv->mtx); ++ ++ if (signal_pending(current)) { ++ finish_wait(&priv->read_wq, &__wait); ++ return (-ERESTARTSYS); ++ } ++ ++ schedule(); ++ ++ res = mutex_lock_interruptible(&priv->mtx); ++ if (res) { ++ finish_wait(&priv->read_wq, &__wait); ++ return (res); ++ } ++ } ++ ++ finish_wait(&priv->read_wq, &__wait); ++ } ++ ++ if (res) ++ goto done; ++ ++ if (count > BOUNCE_SIZE) ++ count = BOUNCE_SIZE; ++ ++ if (!count || (priv->cmd_done && priv->cmd_failed)) ++ goto done; ++ ++ if (copy_to_user(usrbuf, priv->bounce_rbuf + *pos, count)) { ++ res = -EFAULT; ++ goto done; ++ } ++ ++ priv->cmd_data_avail = 0; ++ ++ res = count; ++ ++done: ++ ++ mutex_unlock(&priv->mtx); ++ ++ return (res); ++} ++ ++static ssize_t ps3encdec_write(struct file *file, const char __user *usrbuf, ++ size_t count, loff_t *pos) ++{ ++ struct ps3encdec_private *priv = ps3encdec_priv; ++ struct ps3_storage_device *dev = priv->dev; ++ u32 cmd; ++ int res = 0; ++ ++ if (mutex_lock_interruptible(&priv->mtx)) ++ return (-ERESTARTSYS); ++ ++ if (file->f_flags & O_NONBLOCK) { ++ if (!priv->cmd_done) ++ res = -EAGAIN; ++ } else { ++ DEFINE_WAIT(__wait); ++ ++ while (1) { ++ prepare_to_wait(&priv->write_wq, &__wait, TASK_INTERRUPTIBLE); ++ ++ if (priv->cmd_done) ++ break; ++ ++ mutex_unlock(&priv->mtx); ++ ++ if (signal_pending(current)) { ++ finish_wait(&priv->write_wq, &__wait); ++ return (-ERESTARTSYS); ++ } ++ ++ schedule(); ++ ++ res = mutex_lock_interruptible(&priv->mtx); ++ if (res) { ++ finish_wait(&priv->write_wq, &__wait); ++ return (res); ++ } ++ } ++ ++ finish_wait(&priv->write_wq, &__wait); ++ } ++ ++ if (res) ++ goto done; ++ ++ if (count > BOUNCE_SIZE + sizeof(cmd)) ++ count = BOUNCE_SIZE + sizeof(cmd); ++ ++ if (!count) ++ goto done; ++ ++ if (count < sizeof(cmd)) { ++ res = -EINVAL; ++ goto done; ++ } ++ ++ if (copy_from_user(&cmd, usrbuf, sizeof(cmd))) { ++ res = -EFAULT; ++ goto done; ++ } ++ ++ if (copy_from_user(priv->bounce_wbuf, usrbuf + sizeof(cmd), count - sizeof(cmd))) { ++ res = -EFAULT; ++ goto done; ++ } ++ ++ priv->cmd_done = 0; ++ priv->cmd_failed = 1; ++ priv->cmd_data_avail = 0; ++ ++ res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, ++ priv->bounce_wlpar, count - sizeof(cmd), ++ priv->bounce_rlpar, BOUNCE_SIZE, &dev->tag); ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: res=%d\n", ++ __func__, __LINE__, res); ++ priv->cmd_done = 1; ++ res = -EIO; ++ goto done; ++ } ++ ++ res = count; ++ ++done: ++ ++ mutex_unlock(&priv->mtx); ++ ++ return (res); ++} ++ ++static unsigned int ps3encdec_poll(struct file *file, poll_table *wait) ++{ ++ struct ps3encdec_private *priv = ps3encdec_priv; ++ unsigned int mask = 0; ++ ++ mutex_lock(&priv->mtx); ++ ++ poll_wait(file, &priv->read_wq, wait); ++ poll_wait(file, &priv->write_wq, wait); ++ ++ if (priv->cmd_data_avail) ++ mask |= POLLIN | POLLRDNORM; ++ ++ if (priv->cmd_done) ++ mask |= POLLOUT | POLLWRNORM; ++ ++ mutex_unlock(&priv->mtx); ++ ++ return (mask); ++} ++ ++static irqreturn_t ps3encdec_interrupt(int irq, void *data) ++{ ++ struct ps3_storage_device *dev = data; ++ struct ps3encdec_private *priv; ++ u64 tag, status; ++ int res; ++ ++ res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); ++ ++ pr_info("%s:%d: res=%d status=%llx\n", __func__, __LINE__, res, status); ++ ++ if (tag != dev->tag) { ++ dev_err(&dev->sbd.core, ++ "%s:%u: tag mismatch, got %llx, expected %llx\n", ++ __func__, __LINE__, tag, dev->tag); ++ } ++ ++ if (res) { ++ dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n", ++ __func__, __LINE__, res, status); ++ return (IRQ_HANDLED); ++ } ++ ++ priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ ++ priv->cmd_done = 1; ++ priv->cmd_failed = (status != 0); ++ priv->cmd_data_avail = !priv->cmd_failed; ++ ++ wake_up_interruptible(&priv->read_wq); ++ wake_up_interruptible(&priv->write_wq); ++ ++ return (IRQ_HANDLED); ++} ++ ++static const struct file_operations ps3encdec_fops = { ++ .owner = THIS_MODULE, ++ .open = nonseekable_open, ++ .read = ps3encdec_read, ++ .write = ps3encdec_write, ++ .poll = ps3encdec_poll, ++}; ++ ++static int ps3encdec_probe(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3encdec_private *priv; ++ int res; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return (-ENOMEM); ++ ++ ps3_system_bus_set_drvdata(_dev, priv); ++ ++ dev->bounce_size = BOUNCE_SIZE * 2; ++ dev->bounce_buf = kmalloc(dev->bounce_size, GFP_DMA); ++ if (!dev->bounce_buf) { ++ res = -ENOMEM; ++ goto fail_free_priv; ++ } ++ ++ res = ps3stor_setup(dev, ps3encdec_interrupt); ++ if (res) ++ goto fail_free_bounce; ++ ++ mutex_init(&priv->mtx); ++ ++ init_waitqueue_head(&priv->read_wq); ++ init_waitqueue_head(&priv->write_wq); ++ ++ priv->cmd_done = 1; ++ priv->cmd_failed = 0; ++ priv->cmd_data_avail = 0; ++ ++ priv->misc.minor = MISC_DYNAMIC_MINOR, ++ priv->misc.name = DEVICE_NAME, ++ priv->misc.fops = &ps3encdec_fops, ++ ++ res = misc_register(&priv->misc); ++ if (res) ++ goto fail_teardown; ++ ++ priv->dev = dev; ++ priv->bounce_wbuf = dev->bounce_buf; ++ priv->bounce_wlpar = dev->bounce_lpar; ++ priv->bounce_rbuf = dev->bounce_buf + BOUNCE_SIZE; ++ priv->bounce_rlpar = dev->bounce_lpar + BOUNCE_SIZE; ++ ++ ps3encdec_priv = priv; ++ ++ return (0); ++ ++fail_teardown: ++ ++ ps3stor_teardown(dev); ++ ++fail_free_bounce: ++ ++ kfree(dev->bounce_buf); ++ ++fail_free_priv: ++ ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++ ++ return (res); ++} ++ ++static int ps3encdec_remove(struct ps3_system_bus_device *_dev) ++{ ++ struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); ++ struct ps3encdec_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); ++ ++ ps3encdec_priv = NULL; ++ ++ misc_deregister(&priv->misc); ++ ps3stor_teardown(dev); ++ kfree(dev->bounce_buf); ++ kfree(priv); ++ ps3_system_bus_set_drvdata(_dev, NULL); ++ ++ return (0); ++} ++ ++static struct ps3_system_bus_driver ps3encdec = { ++ .match_id = PS3_MATCH_ID_STOR_ENCDEC, ++ .core.name = DEVICE_NAME, ++ .core.owner = THIS_MODULE, ++ .probe = ps3encdec_probe, ++ .remove = ps3encdec_remove, ++ .shutdown = ps3encdec_remove, ++}; ++ ++static int __init ps3encdec_init(void) ++{ ++ int res; ++ ++ if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) ++ return (-ENODEV); ++ ++ res = ps3_system_bus_driver_register(&ps3encdec); ++ ++ return (res); ++} ++ ++static void __exit ps3encdec_exit(void) ++{ ++ ps3_system_bus_driver_unregister(&ps3encdec); ++} ++ ++module_init(ps3encdec_init); ++module_exit(ps3encdec_exit); ++ ++MODULE_AUTHOR("glevand"); ++MODULE_DESCRIPTION("PS3 ENCDEC Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_ENCDEC); diff --git a/0220-spuisofs.patch b/0220-spuisofs.patch new file mode 100644 index 0000000..1cabe96 --- /dev/null +++ b/0220-spuisofs.patch @@ -0,0 +1,1095 @@ +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-08-08 21:45:40.869511244 +0200 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-08-08 21:43:09.502835770 +0200 +@@ -202,6 +202,13 @@ + This support is required to access the PS3 ENCDEC device. + In general, all users will say Y or M. + ++config SPUISO_FS ++ tristate "PS3 isolated SPU file system" ++ default m ++ depends on PPC_PS3 ++ help ++ The isolated SPU file system is used to execute isolated SPU modules. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- a/arch/powerpc/platforms/ps3/Makefile 2012-08-08 21:45:25.436177008 +0200 ++++ b/arch/powerpc/platforms/ps3/Makefile 2012-08-08 21:43:40.846170928 +0200 +@@ -6,3 +6,5 @@ + obj-$(CONFIG_SMP) += smp.o + obj-$(CONFIG_SPU_BASE) += spu.o + obj-y += device-init.o ++ ++obj-$(CONFIG_SPUISO_FS) += spuisofs.o +--- /dev/null 2013-10-07 20:20:12.741358433 +0200 ++++ b/arch/powerpc/platforms/ps3/spuisofs.c 2013-10-07 22:15:48.445095266 +0200 +@@ -0,0 +1,1068 @@ ++ ++/* ++ * PS3 spuisofs ++ * ++ * Copyright (C) 2012-2013 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define SPUISOFS_MAGIC 0x73707569 ++ ++struct spe_shadow { ++ u8 padding_0140[0x0140]; ++ u64 int_status_class0_RW; /* 0x0140 */ ++ u64 int_status_class1_RW; /* 0x0148 */ ++ u64 int_status_class2_RW; /* 0x0150 */ ++ u8 padding_0158[0x0610-0x0158]; ++ u64 mfc_dsisr_RW; /* 0x0610 */ ++ u8 padding_0618[0x0620-0x0618]; ++ u64 mfc_dar_RW; /* 0x0620 */ ++ u8 padding_0628[0x0800-0x0628]; ++ u64 mfc_dsipr_R; /* 0x0800 */ ++ u8 padding_0808[0x0810-0x0808]; ++ u64 mfc_lscrr_R; /* 0x0810 */ ++ u8 padding_0818[0x0c00-0x0818]; ++ u64 mfc_cer_R; /* 0x0c00 */ ++ u8 padding_0c08[0x0f00-0x0c08]; ++ u64 spe_execution_status; /* 0x0f00 */ ++ u8 padding_0f08[0x1000-0x0f08]; ++}; ++ ++struct spuisofs_inode_info { ++ struct inode vfs_inode; ++ unsigned long io_addr; ++ void *virt_addr; ++}; ++ ++struct spuisofs_tree_descr { ++ const char *name; ++ const struct file_operations *ops; ++ umode_t mode; ++ size_t size; ++ unsigned long io_addr; ++ void *virt_addr; ++}; ++ ++#define SPUISOFS_I(inode) container_of(inode, struct spuisofs_inode_info, vfs_inode) ++ ++struct spuisofs_run_args { ++ u64 laid; ++ u64 arg1_size; ++ u64 arg2_size; ++}; ++ ++static struct kmem_cache *spuisofs_inode_cache; ++ ++static u64 spuisofs_spe_priv2_addr; ++static u64 spuisofs_spe_problem_addr; ++static u64 spuisofs_spe_ls_addr; ++static u64 spuisofs_spe_shadow_addr; ++ ++static void *spuisofs_spe_priv2; ++static struct spu_problem *spuisofs_spe_problem; ++static void *spuisofs_spe_ls; ++static struct spe_shadow *spuisofs_spe_shadow; ++static u64 spuisofs_spe_id; ++static unsigned int spuisofs_spe_virq[4]; ++ ++static void *spuisofs_spe_app; ++static void *spuisofs_spe_arg1; ++static void *spuisofs_spe_arg2; ++static void *spuisofs_spe_buf; ++ ++static unsigned int spuisofs_spe_slb_index; ++ ++static unsigned long spuisofs_spe_app_size = 1024 * 1024; ++module_param(spuisofs_spe_app_size, ulong, 0); ++ ++static unsigned long spuisofs_spe_arg1_size = 1024 * 1024; ++module_param(spuisofs_spe_arg1_size, ulong, 0); ++ ++static unsigned long spuisofs_spe_arg2_size = 1024 * 1024; ++module_param(spuisofs_spe_arg2_size, ulong, 0); ++ ++static unsigned long spuisofs_spe_buf_size = 1024 * 1024; ++module_param(spuisofs_spe_buf_size, ulong, 0); ++ ++static unsigned long spuisofs_spe_trans_notify_mask = 0xf; ++module_param(spuisofs_spe_trans_notify_mask, ulong, 0); ++ ++static unsigned long spuisofs_spe_resource_id = 6; ++module_param(spuisofs_spe_resource_id, ulong, 0); ++ ++static int spuisofs_spe_buf_addr_32bit = 0; ++module_param(spuisofs_spe_buf_addr_32bit, int, 0); ++ ++/* ++ * spuisofs_spe_regs_read ++ */ ++static ssize_t spuisofs_spe_regs_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuisofs_spe_regs_write ++ */ ++static ssize_t spuisofs_spe_regs_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuisofs_spe_regs_mmap ++ */ ++static int spuisofs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ unsigned long size, pfn; ++ ++ size = vma->vm_end - vma->vm_start; ++ pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; ++#else ++ vma->vm_flags |= VM_RESERVED | VM_IO; ++#endif ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); ++} ++ ++static const struct file_operations spuisofs_spe_regs_fops = { ++ .read = spuisofs_spe_regs_read, ++ .write = spuisofs_spe_regs_write, ++ .mmap = spuisofs_spe_regs_mmap, ++}; ++ ++/* ++ * spuisofs_spe_mem_read ++ */ ++static ssize_t spuisofs_spe_mem_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuisofs_spe_mem_write ++ */ ++static ssize_t spuisofs_spe_mem_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuisofs_spe_mem_mmap ++ */ ++static int spuisofs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ unsigned long size, pfn; ++ ++ size = vma->vm_end - vma->vm_start; ++ pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; ++#else ++ vma->vm_flags |= VM_RESERVED | VM_IO; ++#endif ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); ++} ++ ++static const struct file_operations spuisofs_spe_mem_fops = { ++ .read = spuisofs_spe_mem_read, ++ .write = spuisofs_spe_mem_write, ++ .mmap = spuisofs_spe_mem_mmap, ++}; ++ ++/* ++ * spuisofs_mem_read ++ */ ++static ssize_t spuisofs_mem_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuisofs_mem_write ++ */ ++static ssize_t spuisofs_mem_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuisofs_mem_mmap ++ */ ++static int spuisofs_mem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_inode_info *si = SPUISOFS_I(inode); ++ ++ return remap_vmalloc_range(vma, si->virt_addr, 0); ++} ++ ++static const struct file_operations spuisofs_mem_fops = { ++ .read = spuisofs_mem_read, ++ .write = spuisofs_mem_write, ++ .mmap = spuisofs_mem_mmap, ++}; ++ ++/* ++ * spuisofs_info_read ++ */ ++static ssize_t spuisofs_info_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ char buf[256]; ++ size_t len; ++ unsigned long spe_buf_addr; ++ ++ spe_buf_addr = (unsigned long) spuisofs_spe_buf; ++ ++ if (spuisofs_spe_buf_addr_32bit) ++ spe_buf_addr &= 0xfffffffful; ++ ++ len = sprintf(buf, "arg1 %lx\narg2 %lx\nbuf %lx\n", ++ (unsigned long) spuisofs_spe_arg1, (unsigned long) spuisofs_spe_arg2, ++ spe_buf_addr); ++ ++ return simple_read_from_buffer(buffer, size, pos, buf, len); ++} ++ ++static const struct file_operations spuisofs_info_fops = { ++ .read = spuisofs_info_read, ++}; ++ ++/* ++ * spuisofs_run_write ++ */ ++static ssize_t spuisofs_run_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuisofs_run_args args; ++ int err; ++ ++ if (*pos || (size != inode->i_size)) ++ return (-EINVAL); ++ ++ if (copy_from_user(&args, buffer, sizeof(struct spuisofs_run_args))) ++ return -EFAULT; ++ ++ if (args.arg1_size == 0) ++ args.arg1_size = spuisofs_spe_arg1_size; ++ ++ if (args.arg2_size == 0) ++ args.arg2_size = spuisofs_spe_arg2_size; ++ ++ if (args.arg1_size > spuisofs_spe_arg1_size) ++ return (-EINVAL); ++ ++ if (args.arg2_size > spuisofs_spe_arg2_size) ++ return (-EINVAL); ++ ++ err = lv1_undocumented_function_201(spuisofs_spe_id); ++ if (err) ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_201 failed with %d\n", err); ++ ++ err = lv1_undocumented_function_209(spuisofs_spe_id, args.laid, (u64) spuisofs_spe_app, ++ (u64) spuisofs_spe_arg1, args.arg1_size, ++ (u64) spuisofs_spe_arg2, args.arg2_size, spuisofs_spe_resource_id); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_209 failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ return (size); ++} ++ ++static const struct file_operations spuisofs_run_fops = { ++ .write = spuisofs_run_write, ++}; ++ ++/* ++ * spuisofs_cont_write ++ */ ++static ssize_t spuisofs_cont_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ u64 puint_mb_R; ++ int err; ++ ++ err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R); ++ ++ err = lv1_undocumented_function_200(spuisofs_spe_id); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_200 failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ return (size); ++} ++ ++static const struct file_operations spuisofs_cont_fops = { ++ .write = spuisofs_cont_write, ++}; ++ ++/* ++ * spuisofs_alloc_inode ++ */ ++static struct inode *spuisofs_alloc_inode(struct super_block *sb) ++{ ++ struct spuisofs_inode_info *si; ++ ++ si = kmem_cache_alloc(spuisofs_inode_cache, GFP_KERNEL); ++ if (!si) ++ return (NULL); ++ ++ return (&si->vfs_inode); ++} ++ ++/* ++ * spuisofs_i_callback ++ */ ++static void spuisofs_i_callback(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ ++ kmem_cache_free(spuisofs_inode_cache, SPUISOFS_I(inode)); ++} ++ ++/* ++ * spuisofs_destroy_inode ++ */ ++static void spuisofs_destroy_inode(struct inode *inode) ++{ ++ call_rcu(&inode->i_rcu, spuisofs_i_callback); ++} ++ ++/* ++ * spuisofs_init_once ++ */ ++static void spuisofs_init_once(void *p) ++{ ++ struct spuisofs_inode_info *si = p; ++ ++ inode_init_once(&si->vfs_inode); ++} ++ ++/* ++ * spuisofs_new_inode ++ */ ++static struct inode *spuisofs_new_inode(struct super_block *sb, umode_t mode) ++{ ++ struct inode *inode; ++ ++ inode = new_inode(sb); ++ if (!inode) ++ return (NULL); ++ ++ inode->i_ino = get_next_ino(); ++ inode->i_mode = mode; ++ inode->i_uid = current_fsuid(); ++ inode->i_gid = current_fsgid(); ++ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; ++ ++ return (inode); ++} ++ ++/* ++ * spuisofs_setattr ++ */ ++static int spuisofs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ ++ setattr_copy(inode, attr); ++ mark_inode_dirty(inode); ++ ++ return (0); ++} ++ ++static const struct inode_operations spuisofs_inode_ops = { ++ .setattr = spuisofs_setattr, ++}; ++ ++/* ++ * spuisofs_new_file ++ */ ++static int spuisofs_new_file(struct super_block *sb, struct dentry *dentry, ++ const struct file_operations *fops, umode_t mode, size_t size, ++ unsigned long io_addr, void *virt_addr) ++{ ++ struct inode *inode; ++ struct spuisofs_inode_info *si; ++ ++ inode = spuisofs_new_inode(sb, S_IFREG | mode); ++ if (!inode) ++ return (-ENOMEM); ++ ++ inode->i_op = &spuisofs_inode_ops; ++ inode->i_fop = fops; ++ inode->i_size = size; ++ inode->i_private = NULL; ++ ++ si = SPUISOFS_I(inode); ++ si->io_addr = io_addr; ++ si->virt_addr = virt_addr; ++ ++ d_add(dentry, inode); ++ ++ return (0); ++} ++ ++/* ++ * spuisofs_fill_dir ++ */ ++static int spuisofs_fill_dir(struct dentry *dir, ++ const struct spuisofs_tree_descr *files) ++{ ++ struct dentry *dentry, *tmp; ++ int err; ++ ++ while (files->name && files->name[0]) { ++ dentry = d_alloc_name(dir, files->name); ++ if (!dentry) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ err = spuisofs_new_file(dir->d_sb, dentry, files->ops, ++ files->mode, files->size, files->io_addr, files->virt_addr); ++ if (err) ++ goto fail; ++ ++ files++; ++ } ++ ++ return (0); ++ ++fail: ++ ++ list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) ++ dput(dentry); ++ ++ shrink_dcache_parent(dir); ++ ++ return (err); ++} ++ ++static const struct super_operations spuisofs_super_ops = { ++ .alloc_inode = spuisofs_alloc_inode, ++ .destroy_inode = spuisofs_destroy_inode, ++ .statfs = simple_statfs, ++}; ++ ++/* ++ * spuisofs_fill_super ++ */ ++static int spuisofs_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ const struct spuisofs_tree_descr root_dir_contents[] = { ++ { "priv2", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuisofs_spe_priv2_addr, spuisofs_spe_priv2, }, ++ { "problem", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuisofs_spe_problem_addr, spuisofs_spe_problem, }, ++ { "ls", &spuisofs_spe_mem_fops, 0666, LS_SIZE, spuisofs_spe_ls_addr, spuisofs_spe_ls, }, ++ { "shadow", &spuisofs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuisofs_spe_shadow_addr, spuisofs_spe_shadow, }, ++ { "app", &spuisofs_mem_fops, 0666, spuisofs_spe_app_size, 0, spuisofs_spe_app, }, ++ { "arg1", &spuisofs_mem_fops, 0666, spuisofs_spe_arg1_size, 0, spuisofs_spe_arg1, }, ++ { "arg2", &spuisofs_mem_fops, 0666, spuisofs_spe_arg2_size, 0, spuisofs_spe_arg2, }, ++ { "buf", &spuisofs_mem_fops, 0666, spuisofs_spe_buf_size, 0, spuisofs_spe_buf, }, ++ { "info", &spuisofs_info_fops, 0444, 0, 0, NULL, }, ++ { "run", &spuisofs_run_fops, 0222, sizeof(struct spuisofs_run_args), 0, NULL, }, ++ { "cont", &spuisofs_cont_fops, 0222, 0, 0, NULL, }, ++ { }, ++ }; ++ struct inode *root_inode; ++ int err; ++ ++ sb->s_maxbytes = MAX_LFS_FILESIZE; ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = SPUISOFS_MAGIC; ++ sb->s_op = &spuisofs_super_ops; ++ sb->s_time_gran = 1; ++ ++ root_inode = spuisofs_new_inode(sb, S_IFDIR | 0755); ++ if (!root_inode) ++ return (-ENOMEM); ++ ++ root_inode->i_op = &simple_dir_inode_operations; ++ root_inode->i_fop = &simple_dir_operations; ++ ++ /* directory inodes start off with i_nlink == 2 (for "." entry) */ ++ inc_nlink(root_inode); ++ ++ sb->s_root = d_make_root(root_inode); ++ if (!sb->s_root) ++ return (-ENOMEM); ++ ++ err = spuisofs_fill_dir(sb->s_root, root_dir_contents); ++ if (err) ++ return (err); ++ ++ return (0); ++} ++ ++/* ++ * spuisofs_spe_ea_to_kernel_ea ++ */ ++static unsigned long spuisofs_spe_ea_to_kernel_ea(unsigned long spe_ea) ++{ ++ unsigned long kernel_ea, spe_buf_addr; ++ ++ kernel_ea = spe_ea; ++ ++ if (!spuisofs_spe_buf_addr_32bit) ++ return (kernel_ea); ++ ++ spe_buf_addr = (unsigned long) spuisofs_spe_buf & 0xfffffffful; ++ ++ if ((spe_ea >= spe_buf_addr) && (spe_ea < (spe_buf_addr + spuisofs_spe_buf_size))) ++ kernel_ea = (unsigned long) spuisofs_spe_buf + (spe_buf_addr - spe_ea); ++ ++ return (kernel_ea); ++} ++ ++/* ++ * spuisofs_spe_interrupt ++ */ ++static irqreturn_t spuisofs_spe_interrupt(int irq, void *data) ++{ ++ u64 status; ++ u64 ea, kernel_ea, dsisr, esid, vsid; ++ u64 puint_mb_R; ++ u32 spu_status_R; ++ u64 spe_execution_status; ++ int err; ++ ++ if (irq == spuisofs_spe_virq[0]) { ++ printk(KERN_INFO"spuisofs: got class 0 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 0, &status); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuisofs: status %llx\n", status); ++ ++ err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 0, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ } else if (irq == spuisofs_spe_virq[1]) { ++ printk(KERN_INFO"spuisofs: got class 1 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 1, &status); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuisofs: status %llx\n", status); ++ ++ if (status & CLASS1_SEGMENT_FAULT_INTR) { ++ ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW); ++ kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea); ++ ++ esid = (ea & ESID_MASK) | SLB_ESID_V; ++ vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K; ++ ++ printk(KERN_INFO"spuisofs: data segment fault at %llx (%llx)\n", ea, kernel_ea); ++ ++ err = lv1_undocumented_function_62(spuisofs_spe_id, 0, spuisofs_spe_slb_index, esid, vsid); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_62 failed with %d\n", err); ++ goto out; ++ } ++ ++ spuisofs_spe_slb_index++; ++ if (spuisofs_spe_slb_index > SLB_INDEX_MASK) ++ spuisofs_spe_slb_index = 0; ++ } ++ ++ if (status & CLASS1_STORAGE_FAULT_INTR) { ++ ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW); ++ kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea); ++ dsisr = in_be64(&spuisofs_spe_shadow->mfc_dsisr_RW); ++ ++ printk(KERN_INFO"spuisofs: data storage fault at %llx (%llx)\n", ea, kernel_ea); ++ ++ if (dsisr & MFC_DSISR_PTE_NOT_FOUND) { ++ err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300); ++ if (err) { ++ printk(KERN_INFO"spuisofs: hash_page failed with %d\n", err); ++ goto out; ++ } ++ } ++ } ++ ++ err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 1, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ /* restart DMA */ ++ ++ err = lv1_undocumented_function_168(spuisofs_spe_id, 0x3000, 1ull << 32); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_168 failed with %d\n", err); ++ goto out; ++ } ++ } else if (irq == spuisofs_spe_virq[2]) { ++ printk(KERN_INFO"spuisofs: got class 2 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 2, &status); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuisofs: status %llx\n", status); ++ ++ if (status & CLASS2_MAILBOX_INTR) { ++ err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R); ++ } ++ ++ if (status & CLASS2_SPU_STOP_INTR) { ++ spu_status_R = in_be32(&spuisofs_spe_problem->spu_status_R); ++ ++ printk(KERN_INFO"spuisofs: spu_status_R %x\n", spu_status_R); ++ } ++ ++ err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 2, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ } else if (irq == spuisofs_spe_virq[3]) { ++ spe_execution_status = spuisofs_spe_shadow->spe_execution_status; ++ ++ printk(KERN_INFO"spuisofs: transition notification: shadow spe_execution_status %llx\n", ++ spe_execution_status); ++ } else { ++ printk(KERN_INFO"spuisofs: got unknown irq %d\n", irq); ++ } ++ ++out: ++ ++ return (IRQ_HANDLED); ++} ++ ++/* ++ * spuisofs_create_spe ++ */ ++static int spuisofs_create_spe(void) ++{ ++ u64 vas_id, junk; ++ int err; ++ ++ err = lv1_get_virtual_address_space_id_of_ppe(&vas_id); ++ if (err) ++ return (-ENXIO); ++ ++ printk(KERN_INFO"spuisofs: vas id %llu\n", vas_id); ++ ++ err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, ++ PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 2, ++ &spuisofs_spe_priv2_addr, &spuisofs_spe_problem_addr, &spuisofs_spe_ls_addr, ++ &junk, &spuisofs_spe_shadow_addr, &spuisofs_spe_id); ++ if (err) ++ return (-ENXIO); ++ ++ printk(KERN_INFO"spuisofs: spe id %llu\n", spuisofs_spe_id); ++ ++ spuisofs_spe_priv2 = ioremap(spuisofs_spe_priv2_addr, sizeof(struct spu_priv2)); ++ if (!spuisofs_spe_priv2) { ++ err = -ENOMEM; ++ goto fail_destruct_spe; ++ } ++ ++ spuisofs_spe_problem = ioremap(spuisofs_spe_problem_addr, sizeof(struct spu_problem)); ++ if (!spuisofs_spe_problem) { ++ err = -ENOMEM; ++ goto fail_unmap_priv2; ++ } ++ ++ spuisofs_spe_ls = ioremap_prot(spuisofs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE); ++ if (!spuisofs_spe_ls) { ++ err = -ENOMEM; ++ goto fail_unmap_problem; ++ } ++ ++ spuisofs_spe_shadow = __ioremap(spuisofs_spe_shadow_addr, sizeof(struct spe_shadow), ++ _PAGE_NO_CACHE | 3); ++ if (!spuisofs_spe_shadow) { ++ err = -ENOMEM; ++ goto fail_unmap_ls; ++ } ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 0, &spuisofs_spe_virq[0]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_unmap_shadow; ++ } ++ ++ err = request_irq(spuisofs_spe_virq[0], spuisofs_spe_interrupt, 0, ++ "spuisofs_spe_irq0", &spuisofs_spe_virq[0]); ++ if (err) ++ goto fail_destroy_spe_irq_0; ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 1, &spuisofs_spe_virq[1]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_0; ++ } ++ ++ err = request_irq(spuisofs_spe_virq[1], spuisofs_spe_interrupt, 0, ++ "spuisofs_spe_irq1", &spuisofs_spe_virq[1]); ++ if (err) ++ goto fail_destroy_spe_irq_1; ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 2, &spuisofs_spe_virq[2]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_1; ++ } ++ ++ err = request_irq(spuisofs_spe_virq[2], spuisofs_spe_interrupt, 0, ++ "spuisofs_spe_irq2", &spuisofs_spe_virq[2]); ++ if (err) ++ goto fail_destroy_spe_irq_2; ++ ++ err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuisofs_spe_virq[3]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_2; ++ } ++ ++ err = lv1_set_spe_transition_notifier(spuisofs_spe_id, spuisofs_spe_trans_notify_mask, ++ virq_to_hw(spuisofs_spe_virq[3])); ++ if (err) { ++ printk(KERN_INFO"spuisofs: lv1_set_spe_transition_notifier failed with %d\n", err); ++ err = -ENXIO; ++ goto fail_destroy_event_recv_port; ++ } ++ ++ err = request_irq(spuisofs_spe_virq[3], spuisofs_spe_interrupt, 0, ++ "spuisofs_spe_irq3", &spuisofs_spe_virq[3]); ++ if (err) ++ goto fail_destroy_event_recv_port; ++ ++ return (0); ++ ++fail_destroy_event_recv_port: ++ ++ ps3_event_receive_port_destroy(spuisofs_spe_virq[3]); ++ ++fail_free_spe_irq_2: ++ ++ free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]); ++ ++fail_destroy_spe_irq_2: ++ ++ ps3_spe_irq_destroy(spuisofs_spe_virq[2]); ++ ++fail_free_spe_irq_1: ++ ++ free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]); ++ ++fail_destroy_spe_irq_1: ++ ++ ps3_spe_irq_destroy(spuisofs_spe_virq[1]); ++ ++fail_free_spe_irq_0: ++ ++ free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]); ++ ++fail_destroy_spe_irq_0: ++ ++ ps3_spe_irq_destroy(spuisofs_spe_virq[0]); ++ ++fail_unmap_shadow: ++ ++ iounmap(spuisofs_spe_shadow); ++ ++fail_unmap_ls: ++ ++ iounmap(spuisofs_spe_ls); ++ ++fail_unmap_problem: ++ ++ iounmap(spuisofs_spe_problem); ++ ++fail_unmap_priv2: ++ ++ iounmap(spuisofs_spe_priv2); ++ ++fail_destruct_spe: ++ ++ lv1_destruct_logical_spe(spuisofs_spe_id); ++ ++ return (err); ++} ++ ++/* ++ * spuisofs_destruct_spe ++ */ ++static void spuisofs_destruct_spe(void) ++{ ++ free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]); ++ ps3_spe_irq_destroy(spuisofs_spe_virq[0]); ++ ++ free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]); ++ ps3_spe_irq_destroy(spuisofs_spe_virq[1]); ++ ++ free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]); ++ ps3_spe_irq_destroy(spuisofs_spe_virq[2]); ++ ++ free_irq(spuisofs_spe_virq[3], &spuisofs_spe_virq[3]); ++ ps3_event_receive_port_destroy(spuisofs_spe_virq[3]); ++ ++ iounmap(spuisofs_spe_shadow); ++ iounmap(spuisofs_spe_ls); ++ iounmap(spuisofs_spe_problem); ++ iounmap(spuisofs_spe_priv2); ++ ++ lv1_destruct_logical_spe(spuisofs_spe_id); ++} ++ ++/* ++ * spuisofs_mount ++ */ ++static struct dentry *spuisofs_mount(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data) ++{ ++ struct dentry *root; ++ int err; ++ ++ err = spuisofs_create_spe(); ++ if (err) ++ return ERR_PTR(err); ++ ++ spuisofs_spe_app = vmalloc_user(spuisofs_spe_app_size); ++ if (!spuisofs_spe_app) { ++ err = -ENOMEM; ++ goto fail_destruct_spe; ++ } ++ ++ memset(spuisofs_spe_app, 0, spuisofs_spe_app_size); ++ ++ spuisofs_spe_arg1 = vmalloc_user(spuisofs_spe_arg1_size); ++ if (!spuisofs_spe_arg1) { ++ err = -ENOMEM; ++ goto fail_free_spe_app; ++ } ++ ++ memset(spuisofs_spe_arg1, 0, spuisofs_spe_arg1_size); ++ ++ spuisofs_spe_arg2 = vmalloc_user(spuisofs_spe_arg2_size); ++ if (!spuisofs_spe_arg2) { ++ err = -ENOMEM; ++ goto fail_free_spe_arg1; ++ } ++ ++ memset(spuisofs_spe_arg2, 0, spuisofs_spe_arg2_size); ++ ++ spuisofs_spe_buf = vmalloc_user(spuisofs_spe_buf_size); ++ if (!spuisofs_spe_buf) { ++ err = -ENOMEM; ++ goto fail_free_spe_arg2; ++ } ++ ++ memset(spuisofs_spe_buf, 0, spuisofs_spe_buf_size); ++ ++ root = mount_single(fs_type, flags, data, spuisofs_fill_super); ++ if (IS_ERR(root)) { ++ err = PTR_ERR(root); ++ goto fail_free_buf; ++ } ++ ++ return (root); ++ ++fail_free_buf: ++ ++ vfree(spuisofs_spe_buf); ++ ++fail_free_spe_arg2: ++ ++ vfree(spuisofs_spe_arg2); ++ ++fail_free_spe_arg1: ++ ++ vfree(spuisofs_spe_arg1); ++ ++fail_free_spe_app: ++ ++ vfree(spuisofs_spe_app); ++ ++fail_destruct_spe: ++ ++ spuisofs_destruct_spe(); ++ ++ return ERR_PTR(err); ++} ++ ++/* ++ * spuisofs_kill_sb ++ */ ++static void spuisofs_kill_sb(struct super_block *sb) ++{ ++ kill_litter_super(sb); ++ ++ vfree(spuisofs_spe_app); ++ vfree(spuisofs_spe_arg1); ++ vfree(spuisofs_spe_arg2); ++ vfree(spuisofs_spe_buf); ++ spuisofs_destruct_spe(); ++} ++ ++static struct file_system_type spuisofs_type = { ++ .owner = THIS_MODULE, ++ .name = "spuisofs", ++ .mount = spuisofs_mount, ++ .kill_sb = spuisofs_kill_sb, ++}; ++ ++/* ++ * spuisofs_init ++ */ ++static int __init spuisofs_init(void) ++{ ++ int err; ++ ++ spuisofs_inode_cache = kmem_cache_create("spuisofs_inode_cache", ++ sizeof(struct spuisofs_inode_info), 0, SLAB_HWCACHE_ALIGN, ++ spuisofs_init_once); ++ if (!spuisofs_inode_cache) ++ return (-ENOMEM); ++ ++ err = register_filesystem(&spuisofs_type); ++ if (err) ++ goto fail_destroy_inode_cache; ++ ++ return (0); ++ ++fail_destroy_inode_cache: ++ ++ kmem_cache_destroy(spuisofs_inode_cache); ++ ++ return (err); ++} ++ ++/* ++ * spuisofs_exit ++ */ ++static void __exit spuisofs_exit(void) ++{ ++ unregister_filesystem(&spuisofs_type); ++ kmem_cache_destroy(spuisofs_inode_cache); ++} ++ ++module_init(spuisofs_init); ++module_exit(spuisofs_exit); ++ ++MODULE_DESCRIPTION("PS3 spuisofs"); ++MODULE_AUTHOR("glevand"); ++MODULE_LICENSE("GPL"); diff --git a/0230-spuldrfs.patch b/0230-spuldrfs.patch new file mode 100644 index 0000000..e32b33c --- /dev/null +++ b/0230-spuldrfs.patch @@ -0,0 +1,1109 @@ +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-08-16 21:28:29.947236099 +0200 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-08-16 21:30:29.967243082 +0200 +@@ -209,6 +209,13 @@ + help + The isolated SPU file system is used to execute isolated SPU modules. + ++config SPULDR_FS ++ tristate "PS3 isolated SPU loader file system" ++ default m ++ depends on PPC_PS3 ++ help ++ The isolated SPU loader file system is used to execute isolated SPU loaders. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- a/arch/powerpc/platforms/ps3/Makefile 2012-08-16 21:28:29.947236099 +0200 ++++ b/arch/powerpc/platforms/ps3/Makefile 2012-08-16 21:30:54.793911193 +0200 +@@ -8,3 +8,4 @@ + obj-y += device-init.o + + obj-$(CONFIG_SPUISO_FS) += spuisofs.o ++obj-$(CONFIG_SPULDR_FS) += spuldrfs.o +--- /dev/null 2013-10-07 20:20:12.741358433 +0200 ++++ b/arch/powerpc/platforms/ps3/spuldrfs.c 2013-10-07 22:16:03.015096113 +0200 +@@ -0,0 +1,1083 @@ ++ ++/* ++ * PS3 spuldrfs ++ * ++ * Copyright (C) 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define SPULDRFS_MAGIC 0x7370756c ++ ++struct spe_shadow { ++ u8 padding_0140[0x0140]; ++ u64 int_status_class0_RW; /* 0x0140 */ ++ u64 int_status_class1_RW; /* 0x0148 */ ++ u64 int_status_class2_RW; /* 0x0150 */ ++ u8 padding_0158[0x0610-0x0158]; ++ u64 mfc_dsisr_RW; /* 0x0610 */ ++ u8 padding_0618[0x0620-0x0618]; ++ u64 mfc_dar_RW; /* 0x0620 */ ++ u8 padding_0628[0x0800-0x0628]; ++ u64 mfc_dsipr_R; /* 0x0800 */ ++ u8 padding_0808[0x0810-0x0808]; ++ u64 mfc_lscrr_R; /* 0x0810 */ ++ u8 padding_0818[0x0c00-0x0818]; ++ u64 mfc_cer_R; /* 0x0c00 */ ++ u8 padding_0c08[0x0f00-0x0c08]; ++ u64 spe_execution_status; /* 0x0f00 */ ++ u8 padding_0f08[0x1000-0x0f08]; ++}; ++ ++struct spuldrfs_inode_info { ++ struct inode vfs_inode; ++ unsigned long io_addr; ++ void *virt_addr; ++}; ++ ++struct spuldrfs_tree_descr { ++ const char *name; ++ const struct file_operations *ops; ++ umode_t mode; ++ size_t size; ++ unsigned long io_addr; ++ void *virt_addr; ++}; ++ ++#define SPULDRFS_I(inode) container_of(inode, struct spuldrfs_inode_info, vfs_inode) ++ ++static struct kmem_cache *spuldrfs_inode_cache; ++ ++static u64 spuldrfs_spe_priv2_addr; ++static u64 spuldrfs_spe_problem_addr; ++static u64 spuldrfs_spe_ls_addr; ++static u64 spuldrfs_spe_shadow_addr; ++ ++static struct spu_priv2 *spuldrfs_spe_priv2; ++static struct spu_problem *spuldrfs_spe_problem; ++static void *spuldrfs_spe_ls; ++static struct spe_shadow *spuldrfs_spe_shadow; ++static u64 spuldrfs_spe_id; ++static unsigned int spuldrfs_spe_virq[4]; ++ ++static void *spuldrfs_spe_metldr; ++static void *spuldrfs_spe_ldr; ++static void *spuldrfs_spe_buf1; ++static void *spuldrfs_spe_buf2; ++static void *spuldrfs_spe_buf3; ++ ++static unsigned int spuldrfs_spe_slb_index; ++ ++static unsigned long spuldrfs_spe_metldr_size = 1024 * 1024; ++module_param(spuldrfs_spe_metldr_size, ulong, 0); ++ ++static unsigned long spuldrfs_spe_ldr_size = 1024 * 1024; ++module_param(spuldrfs_spe_ldr_size, ulong, 0); ++ ++static unsigned long spuldrfs_spe_buf1_size = 1024 * 1024; ++module_param(spuldrfs_spe_buf1_size, ulong, 0); ++ ++static unsigned long spuldrfs_spe_buf2_size = 1024 * 1024; ++module_param(spuldrfs_spe_buf2_size, ulong, 0); ++ ++static unsigned long spuldrfs_spe_buf3_size = 1024 * 1024; ++module_param(spuldrfs_spe_buf3_size, ulong, 0); ++ ++static unsigned long spuldrfs_spe_trans_notify_mask = 0x7; ++module_param(spuldrfs_spe_trans_notify_mask, ulong, 0); ++ ++static unsigned long spuldrfs_spe_resource_id = 6; ++module_param(spuldrfs_spe_resource_id, ulong, 0); ++ ++static int spuldrfs_spe_buf_addr_32bit = 0; ++module_param(spuldrfs_spe_buf_addr_32bit, int, 0); ++ ++/* ++ * spuldrfs_spe_regs_read ++ */ ++static ssize_t spuldrfs_spe_regs_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuldrfs_spe_regs_write ++ */ ++static ssize_t spuldrfs_spe_regs_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuldrfs_spe_regs_mmap ++ */ ++static int spuldrfs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ unsigned long size, pfn; ++ ++ size = vma->vm_end - vma->vm_start; ++ pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; ++#else ++ vma->vm_flags |= VM_RESERVED | VM_IO; ++#endif ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); ++} ++ ++static const struct file_operations spuldrfs_spe_regs_fops = { ++ .read = spuldrfs_spe_regs_read, ++ .write = spuldrfs_spe_regs_write, ++ .mmap = spuldrfs_spe_regs_mmap, ++}; ++ ++/* ++ * spuldrfs_spe_mem_read ++ */ ++static ssize_t spuldrfs_spe_mem_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuldrfs_spe_mem_write ++ */ ++static ssize_t spuldrfs_spe_mem_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuldrfs_spe_mem_mmap ++ */ ++static int spuldrfs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ unsigned long size, pfn; ++ ++ size = vma->vm_end - vma->vm_start; ++ pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; ++#else ++ vma->vm_flags |= VM_RESERVED | VM_IO; ++#endif ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); ++} ++ ++static const struct file_operations spuldrfs_spe_mem_fops = { ++ .read = spuldrfs_spe_mem_read, ++ .write = spuldrfs_spe_mem_write, ++ .mmap = spuldrfs_spe_mem_mmap, ++}; ++ ++/* ++ * spuldrfs_mem_read ++ */ ++static ssize_t spuldrfs_mem_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (0); ++ ++ return simple_read_from_buffer(buffer, size, pos, ++ si->virt_addr, inode->i_size); ++} ++ ++/* ++ * spuldrfs_mem_write ++ */ ++static ssize_t spuldrfs_mem_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ if (*pos >= inode->i_size) ++ return (-EFBIG); ++ ++ return simple_write_to_buffer(si->virt_addr, inode->i_size, ++ pos, buffer, size); ++} ++ ++/* ++ * spuldrfs_mem_mmap ++ */ ++static int spuldrfs_mem_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ struct spuldrfs_inode_info *si = SPULDRFS_I(inode); ++ ++ return remap_vmalloc_range(vma, si->virt_addr, 0); ++} ++ ++static const struct file_operations spuldrfs_mem_fops = { ++ .read = spuldrfs_mem_read, ++ .write = spuldrfs_mem_write, ++ .mmap = spuldrfs_mem_mmap, ++}; ++ ++/* ++ * spuldrfs_info_read ++ */ ++static ssize_t spuldrfs_info_read(struct file *file, char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ char buf[256]; ++ size_t len; ++ unsigned long spe_buf1_addr, spe_buf2_addr, spe_buf3_addr; ++ ++ spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1; ++ spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2; ++ spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3; ++ ++ if (spuldrfs_spe_buf_addr_32bit) { ++ spe_buf1_addr &= 0xfffffffful; ++ spe_buf2_addr &= 0xfffffffful; ++ spe_buf3_addr &= 0xfffffffful; ++ } ++ ++ len = sprintf(buf, "buf1 %lx\nbuf2 %lx\nbuf3 %lx", ++ spe_buf1_addr, spe_buf2_addr, spe_buf3_addr); ++ ++ return simple_read_from_buffer(buffer, size, pos, buf, len); ++} ++ ++static const struct file_operations spuldrfs_info_fops = { ++ .read = spuldrfs_info_read, ++}; ++ ++/* ++ * spuldrfs_run_write ++ */ ++static ssize_t spuldrfs_run_write(struct file *file, const char __user *buffer, ++ size_t size, loff_t *pos) ++{ ++ int i, err; ++ ++ if (*pos) ++ return (-EINVAL); ++ ++ err = lv1_disable_logical_spe(spuldrfs_spe_id, 0); ++ if (err) ++ printk(KERN_INFO"spuldrfs: lv1_disable_logical_spe failed with %d\n", err); ++ ++ err = lv1_enable_logical_spe(spuldrfs_spe_id, spuldrfs_spe_resource_id); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_enable_logical_spe failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_STOP); ++ ++ /* enable interrupts */ ++ ++ err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 0, 0x7); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 1, 0xf); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 2, 0xf); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ err = lv1_set_spe_privilege_state_area_1_register(spuldrfs_spe_id, offsetof(struct spu_priv1, mfc_sr1_RW), ++ MFC_STATE1_RELOCATE_MASK); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_set_spe_privilege_state_area_1_register failed with %d\n", err); ++ return (-ENXIO); ++ } ++ ++ /* invalidate all SLB entries */ ++ ++ out_be64(&spuldrfs_spe_priv2->slb_invalidate_all_W, 0); ++ ++ for (i = 0; i <= SLB_INDEX_MASK; i++) { ++ out_be64(&spuldrfs_spe_priv2->slb_index_W, i); ++ out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, 0); ++ out_be64(&spuldrfs_spe_priv2->slb_esid_RW, 0); ++ } ++ ++ out_be64(&spuldrfs_spe_priv2->spu_cfg_RW, 0); ++ ++ out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr >> 32); ++ out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr); ++ ++ out_be32(&spuldrfs_spe_problem->signal_notify1, (unsigned long) spuldrfs_spe_metldr >> 32); ++ out_be32(&spuldrfs_spe_problem->signal_notify2, (unsigned long) spuldrfs_spe_metldr); ++ ++ out_be64(&spuldrfs_spe_priv2->spu_privcntl_RW, SPU_PRIVCNT_LOAD_REQUEST_ENABLE_MASK); ++ ++ out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_RUNNABLE); ++ ++ return (size); ++} ++ ++static const struct file_operations spuldrfs_run_fops = { ++ .write = spuldrfs_run_write, ++}; ++ ++/* ++ * spuldrfs_alloc_inode ++ */ ++static struct inode *spuldrfs_alloc_inode(struct super_block *sb) ++{ ++ struct spuldrfs_inode_info *si; ++ ++ si = kmem_cache_alloc(spuldrfs_inode_cache, GFP_KERNEL); ++ if (!si) ++ return (NULL); ++ ++ return (&si->vfs_inode); ++} ++ ++/* ++ * spuldrfs_i_callback ++ */ ++static void spuldrfs_i_callback(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ ++ kmem_cache_free(spuldrfs_inode_cache, SPULDRFS_I(inode)); ++} ++ ++/* ++ * spuldrfs_destroy_inode ++ */ ++static void spuldrfs_destroy_inode(struct inode *inode) ++{ ++ call_rcu(&inode->i_rcu, spuldrfs_i_callback); ++} ++ ++/* ++ * spuldrfs_init_once ++ */ ++static void spuldrfs_init_once(void *p) ++{ ++ struct spuldrfs_inode_info *si = p; ++ ++ inode_init_once(&si->vfs_inode); ++} ++ ++/* ++ * spuldrfs_new_inode ++ */ ++static struct inode *spuldrfs_new_inode(struct super_block *sb, umode_t mode) ++{ ++ struct inode *inode; ++ ++ inode = new_inode(sb); ++ if (!inode) ++ return (NULL); ++ ++ inode->i_ino = get_next_ino(); ++ inode->i_mode = mode; ++ inode->i_uid = current_fsuid(); ++ inode->i_gid = current_fsgid(); ++ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; ++ ++ return (inode); ++} ++ ++/* ++ * spuldrfs_setattr ++ */ ++static int spuldrfs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ ++ setattr_copy(inode, attr); ++ mark_inode_dirty(inode); ++ ++ return (0); ++} ++ ++static const struct inode_operations spuldrfs_inode_ops = { ++ .setattr = spuldrfs_setattr, ++}; ++ ++/* ++ * spuldrfs_new_file ++ */ ++static int spuldrfs_new_file(struct super_block *sb, struct dentry *dentry, ++ const struct file_operations *fops, umode_t mode, size_t size, ++ unsigned long io_addr, void *virt_addr) ++{ ++ struct inode *inode; ++ struct spuldrfs_inode_info *si; ++ ++ inode = spuldrfs_new_inode(sb, S_IFREG | mode); ++ if (!inode) ++ return (-ENOMEM); ++ ++ inode->i_op = &spuldrfs_inode_ops; ++ inode->i_fop = fops; ++ inode->i_size = size; ++ inode->i_private = NULL; ++ ++ si = SPULDRFS_I(inode); ++ si->io_addr = io_addr; ++ si->virt_addr = virt_addr; ++ ++ d_add(dentry, inode); ++ ++ return (0); ++} ++ ++/* ++ * spuldrfs_fill_dir ++ */ ++static int spuldrfs_fill_dir(struct dentry *dir, ++ const struct spuldrfs_tree_descr *files) ++{ ++ struct dentry *dentry, *tmp; ++ int err; ++ ++ while (files->name && files->name[0]) { ++ dentry = d_alloc_name(dir, files->name); ++ if (!dentry) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ ++ err = spuldrfs_new_file(dir->d_sb, dentry, files->ops, ++ files->mode, files->size, files->io_addr, files->virt_addr); ++ if (err) ++ goto fail; ++ ++ files++; ++ } ++ ++ return (0); ++ ++fail: ++ ++ list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) ++ dput(dentry); ++ ++ shrink_dcache_parent(dir); ++ ++ return (err); ++} ++ ++static const struct super_operations spuldrfs_super_ops = { ++ .alloc_inode = spuldrfs_alloc_inode, ++ .destroy_inode = spuldrfs_destroy_inode, ++ .statfs = simple_statfs, ++}; ++ ++/* ++ * spuldrfs_fill_super ++ */ ++static int spuldrfs_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ const struct spuldrfs_tree_descr root_dir_contents[] = { ++ { "priv2", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuldrfs_spe_priv2_addr, spuldrfs_spe_priv2, }, ++ { "problem", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuldrfs_spe_problem_addr, spuldrfs_spe_problem, }, ++ { "ls", &spuldrfs_spe_mem_fops, 0666, LS_SIZE, spuldrfs_spe_ls_addr, spuldrfs_spe_ls, }, ++ { "shadow", &spuldrfs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuldrfs_spe_shadow_addr, spuldrfs_spe_shadow, }, ++ { "metldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_metldr_size, 0, spuldrfs_spe_metldr, }, ++ { "ldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_ldr_size, 0, spuldrfs_spe_ldr, }, ++ { "buf1", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf1_size, 0, spuldrfs_spe_buf1, }, ++ { "buf2", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf2_size, 0, spuldrfs_spe_buf2, }, ++ { "buf3", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf3_size, 0, spuldrfs_spe_buf3, }, ++ { "info", &spuldrfs_info_fops, 0444, 0, 0, NULL, }, ++ { "run", &spuldrfs_run_fops, 0222, 0, 0, NULL, }, ++ { }, ++ }; ++ struct inode *root_inode; ++ int err; ++ ++ sb->s_maxbytes = MAX_LFS_FILESIZE; ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = SPULDRFS_MAGIC; ++ sb->s_op = &spuldrfs_super_ops; ++ sb->s_time_gran = 1; ++ ++ root_inode = spuldrfs_new_inode(sb, S_IFDIR | 0755); ++ if (!root_inode) ++ return (-ENOMEM); ++ ++ root_inode->i_op = &simple_dir_inode_operations; ++ root_inode->i_fop = &simple_dir_operations; ++ ++ /* directory inodes start off with i_nlink == 2 (for "." entry) */ ++ inc_nlink(root_inode); ++ ++ sb->s_root = d_make_root(root_inode); ++ if (!sb->s_root) ++ return (-ENOMEM); ++ ++ err = spuldrfs_fill_dir(sb->s_root, root_dir_contents); ++ if (err) ++ return (err); ++ ++ return (0); ++} ++ ++/* ++ * spuldrfs_spe_ea_to_kernel_ea ++ */ ++static unsigned long spuldrfs_spe_ea_to_kernel_ea(unsigned long spe_ea) ++{ ++ unsigned long kernel_ea, spe_buf1_addr, spe_buf2_addr, spe_buf3_addr; ++ ++ kernel_ea = spe_ea; ++ ++ if (!spuldrfs_spe_buf_addr_32bit) ++ return (kernel_ea); ++ ++ spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1 & 0xfffffffful; ++ spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2 & 0xfffffffful; ++ spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3 & 0xfffffffful; ++ ++ if ((spe_ea >= spe_buf1_addr) && (spe_ea < (spe_buf1_addr + spuldrfs_spe_buf1_size))) ++ kernel_ea = (unsigned long) spuldrfs_spe_buf1 + (spe_buf1_addr - spe_ea); ++ else if ((spe_ea >= spe_buf2_addr) && (spe_ea < (spe_buf2_addr + spuldrfs_spe_buf2_size))) ++ kernel_ea = (unsigned long) spuldrfs_spe_buf2 + (spe_buf2_addr - spe_ea); ++ else if ((spe_ea >= spe_buf3_addr) && (spe_ea < (spe_buf3_addr + spuldrfs_spe_buf3_size))) ++ kernel_ea = (unsigned long) spuldrfs_spe_buf3 + (spe_buf3_addr - spe_ea); ++ ++ return (kernel_ea); ++} ++ ++/* ++ * spuldrfs_spe_interrupt ++ */ ++static irqreturn_t spuldrfs_spe_interrupt(int irq, void *data) ++{ ++ u64 status; ++ u64 ea, kernel_ea, dsisr, esid, vsid; ++ u64 puint_mb_R; ++ u32 spu_status_R; ++ u64 spe_execution_status; ++ int err; ++ ++ if (irq == spuldrfs_spe_virq[0]) { ++ printk(KERN_INFO"spuldrfs: got class 0 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 0, &status); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuldrfs: status %llx\n", status); ++ ++ err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 0, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ } else if (irq == spuldrfs_spe_virq[1]) { ++ printk(KERN_INFO"spuldrfs: got class 1 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 1, &status); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuldrfs: status %llx\n", status); ++ ++ if (status & CLASS1_SEGMENT_FAULT_INTR) { ++ ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW); ++ kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea); ++ ++ esid = (ea & ESID_MASK) | SLB_ESID_V; ++ vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K; ++ ++ printk(KERN_INFO"spuldrfs: data segment fault at %llx (%llx)\n", ea, kernel_ea); ++ ++ out_be64(&spuldrfs_spe_priv2->slb_index_W, spuldrfs_spe_slb_index); ++ out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, vsid); ++ out_be64(&spuldrfs_spe_priv2->slb_esid_RW, esid); ++ ++ spuldrfs_spe_slb_index++; ++ if (spuldrfs_spe_slb_index > SLB_INDEX_MASK) ++ spuldrfs_spe_slb_index = 0; ++ } ++ ++ if (status & CLASS1_STORAGE_FAULT_INTR) { ++ ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW); ++ kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea); ++ dsisr = in_be64(&spuldrfs_spe_shadow->mfc_dsisr_RW); ++ ++ printk(KERN_INFO"spuldrfs: data storage fault at %llx (%llx)\n", ea, kernel_ea); ++ ++ if (dsisr & MFC_DSISR_PTE_NOT_FOUND) { ++ err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: hash_page failed with %d\n", err); ++ goto out; ++ } ++ } ++ } ++ ++ err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 1, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ /* restart DMA */ ++ ++ out_be64(&spuldrfs_spe_priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); ++ } else if (irq == spuldrfs_spe_virq[2]) { ++ printk(KERN_INFO"spuldrfs: got class 2 irq\n"); ++ ++ err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 2, &status); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ ++ printk(KERN_INFO"spuldrfs: status %llx\n", status); ++ ++ if (status & CLASS2_MAILBOX_INTR) { ++ puint_mb_R = in_be64(&spuldrfs_spe_priv2->puint_mb_R); ++ ++ printk(KERN_INFO"spuldrfs: puint_mb_R %llx\n", puint_mb_R); ++ } ++ ++ if (status & CLASS2_SPU_STOP_INTR) { ++ spu_status_R = in_be32(&spuldrfs_spe_problem->spu_status_R); ++ ++ printk(KERN_INFO"spuldrfs: spu_status_R %x\n", spu_status_R); ++ } ++ ++ err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 2, status, 0); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err); ++ goto out; ++ } ++ } else if (irq == spuldrfs_spe_virq[3]) { ++ spe_execution_status = spuldrfs_spe_shadow->spe_execution_status; ++ ++ printk(KERN_INFO"spuldrfs: transition notification: shadow spe_execution_status %llx\n", ++ spe_execution_status); ++ } else { ++ printk(KERN_INFO"spuldrfs: got unknown irq %d\n", irq); ++ } ++ ++out: ++ ++ return (IRQ_HANDLED); ++} ++ ++/* ++ * spuldrfs_create_spe ++ */ ++static int spuldrfs_create_spe(void) ++{ ++ u64 vas_id, junk; ++ int err; ++ ++ err = lv1_get_virtual_address_space_id_of_ppe(&vas_id); ++ if (err) ++ return (-ENXIO); ++ ++ printk(KERN_INFO"spuldrfs: vas id %llu\n", vas_id); ++ ++ err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, ++ PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 0, ++ &spuldrfs_spe_priv2_addr, &spuldrfs_spe_problem_addr, &spuldrfs_spe_ls_addr, ++ &junk, &spuldrfs_spe_shadow_addr, &spuldrfs_spe_id); ++ if (err) ++ return (-ENXIO); ++ ++ printk(KERN_INFO"spuldrfs: spe id %llu\n", spuldrfs_spe_id); ++ ++ spuldrfs_spe_priv2 = ioremap(spuldrfs_spe_priv2_addr, sizeof(struct spu_priv2)); ++ if (!spuldrfs_spe_priv2) { ++ err = -ENOMEM; ++ goto fail_destruct_spe; ++ } ++ ++ spuldrfs_spe_problem = ioremap(spuldrfs_spe_problem_addr, sizeof(struct spu_problem)); ++ if (!spuldrfs_spe_problem) { ++ err = -ENOMEM; ++ goto fail_unmap_priv2; ++ } ++ ++ spuldrfs_spe_ls = ioremap_prot(spuldrfs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE); ++ if (!spuldrfs_spe_ls) { ++ err = -ENOMEM; ++ goto fail_unmap_problem; ++ } ++ ++ spuldrfs_spe_shadow = __ioremap(spuldrfs_spe_shadow_addr, sizeof(struct spe_shadow), ++ _PAGE_NO_CACHE | 3); ++ if (!spuldrfs_spe_shadow) { ++ err = -ENOMEM; ++ goto fail_unmap_ls; ++ } ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 0, &spuldrfs_spe_virq[0]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_unmap_shadow; ++ } ++ ++ err = request_irq(spuldrfs_spe_virq[0], spuldrfs_spe_interrupt, 0, ++ "spuldrfs_spe_irq0", &spuldrfs_spe_virq[0]); ++ if (err) ++ goto fail_destroy_spe_irq_0; ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 1, &spuldrfs_spe_virq[1]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_0; ++ } ++ ++ err = request_irq(spuldrfs_spe_virq[1], spuldrfs_spe_interrupt, 0, ++ "spuldrfs_spe_irq1", &spuldrfs_spe_virq[1]); ++ if (err) ++ goto fail_destroy_spe_irq_1; ++ ++ err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 2, &spuldrfs_spe_virq[2]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_1; ++ } ++ ++ err = request_irq(spuldrfs_spe_virq[2], spuldrfs_spe_interrupt, 0, ++ "spuldrfs_spe_irq2", &spuldrfs_spe_virq[2]); ++ if (err) ++ goto fail_destroy_spe_irq_2; ++ ++ err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuldrfs_spe_virq[3]); ++ if (err) { ++ err = -ENXIO; ++ goto fail_free_spe_irq_2; ++ } ++ ++ err = lv1_set_spe_transition_notifier(spuldrfs_spe_id, spuldrfs_spe_trans_notify_mask, ++ virq_to_hw(spuldrfs_spe_virq[3])); ++ if (err) { ++ printk(KERN_INFO"spuldrfs: lv1_set_spe_transition_notifier failed with %d\n", err); ++ err = -ENXIO; ++ goto fail_destroy_event_recv_port; ++ } ++ ++ err = request_irq(spuldrfs_spe_virq[3], spuldrfs_spe_interrupt, 0, ++ "spuldrfs_spe_irq3", &spuldrfs_spe_virq[3]); ++ if (err) ++ goto fail_destroy_event_recv_port; ++ ++ return (0); ++ ++fail_destroy_event_recv_port: ++ ++ ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]); ++ ++fail_free_spe_irq_2: ++ ++ free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]); ++ ++fail_destroy_spe_irq_2: ++ ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[2]); ++ ++fail_free_spe_irq_1: ++ ++ free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]); ++ ++fail_destroy_spe_irq_1: ++ ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[1]); ++ ++fail_free_spe_irq_0: ++ ++ free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]); ++ ++fail_destroy_spe_irq_0: ++ ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[0]); ++ ++fail_unmap_shadow: ++ ++ iounmap(spuldrfs_spe_shadow); ++ ++fail_unmap_ls: ++ ++ iounmap(spuldrfs_spe_ls); ++ ++fail_unmap_problem: ++ ++ iounmap(spuldrfs_spe_problem); ++ ++fail_unmap_priv2: ++ ++ iounmap(spuldrfs_spe_priv2); ++ ++fail_destruct_spe: ++ ++ lv1_destruct_logical_spe(spuldrfs_spe_id); ++ ++ return (err); ++} ++ ++/* ++ * spuldrfs_destruct_spe ++ */ ++static void spuldrfs_destruct_spe(void) ++{ ++ lv1_disable_logical_spe(spuldrfs_spe_id, 0); ++ ++ free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]); ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[0]); ++ ++ free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]); ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[1]); ++ ++ free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]); ++ ps3_spe_irq_destroy(spuldrfs_spe_virq[2]); ++ ++ free_irq(spuldrfs_spe_virq[3], &spuldrfs_spe_virq[3]); ++ ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]); ++ ++ iounmap(spuldrfs_spe_shadow); ++ iounmap(spuldrfs_spe_ls); ++ iounmap(spuldrfs_spe_problem); ++ iounmap(spuldrfs_spe_priv2); ++ ++ lv1_destruct_logical_spe(spuldrfs_spe_id); ++} ++ ++/* ++ * spuldrfs_mount ++ */ ++static struct dentry *spuldrfs_mount(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data) ++{ ++ struct dentry *root; ++ int err; ++ ++ err = spuldrfs_create_spe(); ++ if (err) ++ return ERR_PTR(err); ++ ++ spuldrfs_spe_metldr = vmalloc_user(spuldrfs_spe_metldr_size); ++ if (!spuldrfs_spe_metldr) { ++ err = -ENOMEM; ++ goto fail_destruct_spe; ++ } ++ ++ memset(spuldrfs_spe_metldr, 0, spuldrfs_spe_metldr_size); ++ ++ spuldrfs_spe_ldr = vmalloc_user(spuldrfs_spe_ldr_size); ++ if (!spuldrfs_spe_ldr) { ++ err = -ENOMEM; ++ goto fail_free_spe_metldr; ++ } ++ ++ memset(spuldrfs_spe_ldr, 0, spuldrfs_spe_ldr_size); ++ ++ spuldrfs_spe_buf1 = vmalloc_user(spuldrfs_spe_buf1_size); ++ if (!spuldrfs_spe_buf1) { ++ err = -ENOMEM; ++ goto fail_free_spe_ldr; ++ } ++ ++ memset(spuldrfs_spe_buf1, 0, spuldrfs_spe_buf1_size); ++ ++ spuldrfs_spe_buf2 = vmalloc_user(spuldrfs_spe_buf2_size); ++ if (!spuldrfs_spe_buf2) { ++ err = -ENOMEM; ++ goto fail_free_spe_buf1; ++ } ++ ++ memset(spuldrfs_spe_buf2, 0, spuldrfs_spe_buf2_size); ++ ++ spuldrfs_spe_buf3 = vmalloc_user(spuldrfs_spe_buf3_size); ++ if (!spuldrfs_spe_buf3) { ++ err = -ENOMEM; ++ goto fail_free_spe_buf2; ++ } ++ ++ memset(spuldrfs_spe_buf3, 0, spuldrfs_spe_buf3_size); ++ ++ root = mount_single(fs_type, flags, data, spuldrfs_fill_super); ++ if (IS_ERR(root)) { ++ err = PTR_ERR(root); ++ goto fail_free_spe_buf3; ++ } ++ ++ return (root); ++ ++fail_free_spe_buf3: ++ ++ vfree(spuldrfs_spe_buf3); ++ ++fail_free_spe_buf2: ++ ++ vfree(spuldrfs_spe_buf2); ++ ++fail_free_spe_buf1: ++ ++ vfree(spuldrfs_spe_buf1); ++ ++fail_free_spe_ldr: ++ ++ vfree(spuldrfs_spe_ldr); ++ ++fail_free_spe_metldr: ++ ++ vfree(spuldrfs_spe_metldr); ++ ++fail_destruct_spe: ++ ++ spuldrfs_destruct_spe(); ++ ++ return ERR_PTR(err); ++} ++ ++/* ++ * spuldrfs_kill_sb ++ */ ++static void spuldrfs_kill_sb(struct super_block *sb) ++{ ++ kill_litter_super(sb); ++ ++ vfree(spuldrfs_spe_metldr); ++ vfree(spuldrfs_spe_ldr); ++ vfree(spuldrfs_spe_buf1); ++ vfree(spuldrfs_spe_buf2); ++ vfree(spuldrfs_spe_buf3); ++ spuldrfs_destruct_spe(); ++} ++ ++static struct file_system_type spuldrfs_type = { ++ .owner = THIS_MODULE, ++ .name = "spuldrfs", ++ .mount = spuldrfs_mount, ++ .kill_sb = spuldrfs_kill_sb, ++}; ++ ++/* ++ * spuldrfs_init ++ */ ++static int __init spuldrfs_init(void) ++{ ++ int err; ++ ++ spuldrfs_inode_cache = kmem_cache_create("spuldrfs_inode_cache", ++ sizeof(struct spuldrfs_inode_info), 0, SLAB_HWCACHE_ALIGN, ++ spuldrfs_init_once); ++ if (!spuldrfs_inode_cache) ++ return (-ENOMEM); ++ ++ err = register_filesystem(&spuldrfs_type); ++ if (err) ++ goto fail_destroy_inode_cache; ++ ++ return (0); ++ ++fail_destroy_inode_cache: ++ ++ kmem_cache_destroy(spuldrfs_inode_cache); ++ ++ return (err); ++} ++ ++/* ++ * spuldrfs_exit ++ */ ++static void __exit spuldrfs_exit(void) ++{ ++ unregister_filesystem(&spuldrfs_type); ++ kmem_cache_destroy(spuldrfs_inode_cache); ++} ++ ++module_init(spuldrfs_init); ++module_exit(spuldrfs_exit); ++ ++MODULE_DESCRIPTION("PS3 spuldrfs"); ++MODULE_AUTHOR("glevand"); ++MODULE_LICENSE("GPL"); diff --git a/0240-ps3lv1call.patch b/0240-ps3lv1call.patch new file mode 100644 index 0000000..6490aff --- /dev/null +++ b/0240-ps3lv1call.patch @@ -0,0 +1,247 @@ +--- a/drivers/char/Makefile 2012-10-27 02:15:23.000000000 -0800 ++++ b/drivers/char/Makefile 2012-10-27 02:16:28.000000000 -0800 +@@ -67,3 +67,4 @@ + obj-$(CONFIG_PS3_PHYSMEM) += ps3physmem.o + obj-$(CONFIG_PS3_STRGMNGR) += ps3strgmngr.o + obj-$(CONFIG_PS3_ENCDEC) += ps3encdec.o ++obj-$(CONFIG_PS3_LV1CALL) += ps3lv1call/ +--- a/arch/powerpc/platforms/ps3/Kconfig 2012-10-27 02:11:58.000000000 -0800 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2012-10-27 02:13:08.000000000 -0800 +@@ -228,6 +228,12 @@ + help + The isolated SPU loader file system is used to execute isolated SPU loaders. + ++config PS3_LV1CALL ++ tristate "PS3 LV1 Call Driver" ++ depends on PPC_PS3 ++ help ++ This driver allows you to execute LV1 calls. ++ + config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 +--- /dev/null 2012-10-27 01:55:30.097760558 -0800 ++++ b/drivers/char/ps3lv1call/Makefile 2012-10-27 02:10:35.000000000 -0800 +@@ -0,0 +1,4 @@ ++ ++ps3lv1call-y := ps3lv1call_misc.o generic_lv1call.o ++ ++obj-$(CONFIG_PS3_LV1CALL) += ps3lv1call.o +--- /dev/null 2012-10-27 01:55:30.097760558 -0800 ++++ b/drivers/char/ps3lv1call/generic_lv1call.S 2012-10-27 02:08:57.000000000 -0800 +@@ -0,0 +1,54 @@ ++/* ++ * PS3 Generic LV1 Call ++ * ++ * Copyright (C) 2012 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++ ++#define lv1call .long 0x44000022; extsw r3, r3 ++ ++_GLOBAL(generic_lv1call) ++ mflr r0 ++ std r0, 16(r1) ++ std r3, -8(r1) ++ std r4, -16(r1) ++ stdu r5, -24(r1) ++ mr r11, r4 ++ ld r3, 0(r11) ++ ld r4, 8(r11) ++ ld r5, 16(r11) ++ ld r6, 24(r11) ++ ld r7, 32(r11) ++ ld r8, 40(r11) ++ ld r9, 48(r11) ++ ld r10, 56(r11) ++ ld r11, 16(r1) ++ lv1call ++ addi r1, r1, 24 ++ ld r11, -24(r1) ++ std r4, 0(r11) ++ std r5, 8(r11) ++ std r6, 16(r11) ++ std r7, 24(r11) ++ std r8, 32(r11) ++ std r9, 40(r11) ++ std r10, 48(r11) ++ ld r0, 16(r1) ++ mtlr r0 ++ blr +--- /dev/null 2013-02-13 19:35:09.393146190 +0100 ++++ b/drivers/char/ps3lv1call/ps3lv1call_misc.c 2013-02-12 20:22:36.473200805 +0100 +@@ -0,0 +1,158 @@ ++/* ++ * PS3 LV1 Call ++ * ++ * Copyright (C) 2012, 2013 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct ++{ ++ int num_in_args; ++ int num_out_args; ++} ps3lv1call_args[256] = { ++ [8] = { 0, 1 }, /* lv1_undocumented_function_8 */ ++ [44] = { 1, 0 }, /* lv1_shutdown_logical_partition */ ++ [91] = { 5, 2 }, /* lv1_read_repository_node */ ++ [102] = { 0, 1 }, /* lv1_undocumented_function_102 */ ++ [105] = { 7, 0 }, /* lv1_undocumented_function_105 */ ++ [106] = { 1, 0 }, /* lv1_undocumented_function_106 */ ++ [107] = { 6, 1 }, /* lv1_undocumented_function_107 */ ++ [108] = { 1, 5 }, /* lv1_undocumented_function_108 */ ++ [109] = { 1, 0 }, /* lv1_undocumented_function_109 */ ++ [127] = { 0, 2 }, /* lv1_get_version_info */ ++ [194] = { 6, 2 }, /* lv1_net_control */ ++ [231] = { 1, 0 }, /* lv1_undocumented_function_231 */ ++ [232] = { 0, 2 }, /* lv1_get_rtc */ ++ [255] = { 1, 0 }, /* lv1_panic */ ++}; ++ ++extern int generic_lv1call(int num_lv1call, u64 *in_args, u64 *out_args); ++ ++static ssize_t ps3lv1call_do_call(char *buf, size_t size) ++{ ++ u64 num_lv1call; ++ u64 args[8]; ++ s64 err; ++ int num_args, i, rv; ++ ++ if (size < sizeof(u64)) ++ return (-EINVAL); ++ ++ if (size % sizeof(u64)) ++ return (-EINVAL); ++ ++ num_lv1call = *(u64 *) buf; ++ if (num_lv1call > 255) ++ return (-EINVAL); ++ ++ num_args = (size - sizeof(u64)) / sizeof(u64); ++ if (num_args > 8) ++ return (-EINVAL); ++ ++ memset(args, 0, sizeof(args)); ++ ++ for (i = 0; i < num_args; i++) ++ args[i] = *(u64 *) (buf + (i + 1) * sizeof(u64)); ++ ++ err = generic_lv1call(num_lv1call, args, args); ++ ++ rv = sizeof(err); ++ memcpy(buf, &err, sizeof(err)); ++ ++ if (!err) { ++ rv += ps3lv1call_args[num_lv1call].num_out_args * sizeof(u64); ++ memcpy(buf + sizeof(err), args, ++ ps3lv1call_args[num_lv1call].num_out_args * sizeof(u64)); ++ } ++ ++ return (rv); ++} ++ ++static ssize_t ps3lv1call_write(struct file *file, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t rv; ++ ++ data = simple_transaction_get(file, buf, size); ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ rv = ps3lv1call_do_call(data, size); ++ if (rv >= 0) { ++ simple_transaction_set(file, rv); ++ rv = size; ++ } ++ ++ return (rv); ++} ++ ++static ssize_t ps3lv1call_read(struct file *file, char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t rv; ++ ++ if (!file->private_data) { ++ rv = ps3lv1call_write(file, buf, 0, pos); ++ if (rv < 0) ++ return (rv); ++ } ++ ++ return simple_transaction_read(file, buf, size, pos); ++} ++ ++static const struct file_operations ps3lv1call_fops = { ++ .owner = THIS_MODULE, ++ .write = ps3lv1call_write, ++ .read = ps3lv1call_read, ++ .release = simple_transaction_release, ++ .llseek = default_llseek, ++}; ++ ++static struct miscdevice ps3lv1call_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ps3lv1call", ++ .fops = &ps3lv1call_fops, ++}; ++ ++static int __init ps3lv1call_init(void) ++{ ++ int err; ++ ++ err = misc_register(&ps3lv1call_misc); ++ ++ return (err); ++} ++ ++static void __exit ps3lv1call_exit(void) ++{ ++ misc_deregister(&ps3lv1call_misc); ++} ++ ++module_init(ps3lv1call_init); ++module_exit(ps3lv1call_exit); ++ ++MODULE_AUTHOR("glevand"); ++MODULE_DESCRIPTION("PS3 LV1 Call"); ++MODULE_LICENSE("GPL"); diff --git a/0250-lv1call-add-debug-console-hvcalls.patch b/0250-lv1call-add-debug-console-hvcalls.patch new file mode 100644 index 0000000..c554c5d --- /dev/null +++ b/0250-lv1call-add-debug-console-hvcalls.patch @@ -0,0 +1,14 @@ +--- a/arch/powerpc/include/asm/lv1call.h 2013-02-21 14:04:37.204534305 +0100 ++++ b/arch/powerpc/include/asm/lv1call.h 2013-02-21 14:12:07.299635321 +0100 +@@ -274,6 +274,11 @@ + LV1_CALL(set_dabr, 2, 0, 96 ) + LV1_CALL(undocumented_function_99, 2, 0, 99 ) + LV1_CALL(get_total_execution_time, 2, 1, 103 ) ++LV1_CALL(undocumented_function_105, 7, 0, 105 ) ++LV1_CALL(undocumented_function_106, 1, 0, 106 ) ++LV1_CALL(undocumented_function_107, 6, 1, 107 ) ++LV1_CALL(undocumented_function_108, 1, 5, 108 ) ++LV1_CALL(undocumented_function_109, 1, 0, 109 ) + LV1_CALL(undocumented_function_114, 3, 1, 114 ) + LV1_CALL(undocumented_function_115, 1, 0, 115 ) + LV1_CALL(allocate_io_segment, 3, 1, 116 ) diff --git a/0260-udbg-lv1-console.patch b/0260-udbg-lv1-console.patch new file mode 100644 index 0000000..cb38ef7 --- /dev/null +++ b/0260-udbg-lv1-console.patch @@ -0,0 +1,123 @@ +--- a/arch/powerpc/platforms/ps3/Kconfig 2013-02-21 13:59:28.585037559 +0100 ++++ b/arch/powerpc/platforms/ps3/Kconfig 2013-02-21 14:01:19.724894506 +0100 +@@ -246,4 +246,12 @@ + + If in doubt, say N here. + ++config PS3_LV1_CONS_UDBG ++ bool "PS3 udbg output via LV1 console" ++ depends on PPC_PS3 ++ help ++ Enables udbg early debugging output to LV1 console. ++ ++ If in doubt, say N here. ++ + endmenu +--- a/arch/powerpc/platforms/ps3/Makefile 2013-02-21 14:02:49.992787219 +0100 ++++ b/arch/powerpc/platforms/ps3/Makefile 2013-02-21 14:03:33.870050887 +0100 +@@ -3,6 +3,7 @@ + obj-y += system-bus.o + + obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o ++obj-$(CONFIG_PS3_LV1_CONS_UDBG) += lv1_cons_udbg.o + obj-$(CONFIG_SMP) += smp.o + obj-$(CONFIG_SPU_BASE) += spu.o + obj-y += device-init.o +--- /dev/null 2012-11-28 15:48:49.557690341 +0100 ++++ b/arch/powerpc/platforms/ps3/lv1_cons_udbg.c 2013-02-21 14:23:49.250083391 +0100 +@@ -0,0 +1,57 @@ ++/* ++ * PS3 LV1 Debug Console ++ * ++ * Copyright (C) 2013 glevand ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++ ++#define LV1_CONS_ID 1 ++#define LV1_CONS_LENGTH 0xff0 ++ ++static int initialized = 0; ++ ++static void lv1_cons_udbg_putc(char ch) ++{ ++ u64 data, written; ++ int ret; ++ ++ if (!initialized) { ++ ret = lv1_undocumented_function_105(LV1_CONS_ID, 0, 0, ++ LV1_CONS_LENGTH, LV1_CONS_LENGTH, 0, 0); ++ if ((ret != 0) && (ret != -7)) ++ return; ++ ++ initialized = 1; ++ } ++ ++ data = ch; ++ data <<= 56; ++ ++ lv1_undocumented_function_107(LV1_CONS_ID, 1, data, 0, 0, 0, &written); ++ ++ /* flush to console buffer in LV1 */ ++ ++ lv1_undocumented_function_109(LV1_CONS_ID); ++} ++ ++void __init udbg_init_ps3_lv1_cons(void) ++{ ++ udbg_putc = lv1_cons_udbg_putc; ++} +--- a/arch/powerpc/Kconfig.debug 2013-02-21 14:31:39.023621546 +0100 ++++ b/arch/powerpc/Kconfig.debug 2013-02-21 14:32:47.370765998 +0100 +@@ -248,6 +248,14 @@ + Select this to enable early debugging for the PlayStation3 via + UDP broadcasts sent out through the Ethernet port. + ++config PPC_EARLY_DEBUG_PS3_LV1_CONS ++ bool "Early debugging through the PS3 LV1 console" ++ depends on PPC_PS3 ++ select PS3_LV1_CONS_UDBG ++ help ++ Select this to enable early debugging for the PlayStation3 via ++ LV1 debug console. ++ + config PPC_EARLY_DEBUG_OPAL_RAW + bool "OPAL raw console" + depends on HVC_OPAL +--- a/arch/powerpc/include/asm/udbg.h 2013-02-21 14:15:53.855871374 +0100 ++++ b/arch/powerpc/include/asm/udbg.h 2013-02-21 14:16:16.107616876 +0100 +@@ -54,6 +54,7 @@ + extern void __init udbg_init_wsp(void); + extern void __init udbg_init_ehv_bc(void); + extern void __init udbg_init_ps3gelic(void); ++extern void __init udbg_init_ps3_lv1_cons(void); + extern void __init udbg_init_debug_opal_raw(void); + extern void __init udbg_init_debug_opal_hvsi(void); + +--- a/arch/powerpc/kernel/udbg.c 2013-02-21 14:14:28.283621378 +0100 ++++ b/arch/powerpc/kernel/udbg.c 2013-02-21 14:15:16.431624882 +0100 +@@ -68,6 +68,8 @@ + udbg_init_ehv_bc(); + #elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC) + udbg_init_ps3gelic(); ++#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3_LV1_CONS) ++ udbg_init_ps3_lv1_cons(); + #elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_RAW) + udbg_init_debug_opal_raw(); + #elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI) -- 2.11.4.GIT